A quick intro to the intro to R Lesson Series


This ‘Intro to R Lesson Series’ is brought to you by the Centre for the Analysis of Genome Evolution & Function’s (CAGEF) bioinformatics training initiative. This course was developed based on feedback on the needs and interests of the Department of Cell & Systems Biology and the Department of Ecology and Evolutionary Biology.

This lesson is the third in a 6-part series. The idea is that at the end of the series, you will be able to import and manipulate your data, make exploratory plots, perform some basic statistical tests, test a regression model, and make some even prettier plots and documents to share your results.


How do we get there? Today we are going to be learning how to make all sorts of plots - from simple data exploration to interactive plots.The next lesson will be data cleaning and string manipulation; this is really the battleground of coding - getting your data into the format where you can analyse it. Then we will learn how to do t-tests and perfrom regression and modeling in R. And lastly, we will learn to write some functions, which really can save you time and help scale up your analyses.


The structure of the class is a code-along style. It is hands on. The lecture AND code we are going through are available on GitHub for download at https://github.com/eacton/CAGEF (Note: repo is private until approved), so you can spend the time coding and not taking notes. As we go along, there will be some challenge questions and multiple choice questions on Socrative. At the end of the class if you could please fill out a post-lesson survey (https://www.surveymonkey.com/r/VNQZ3KS), it will help me further develop this course and would be greatly appreciated.



Objective: At the end of this session you will be able to use ggplot2() to make a ton of different types of plots with your data for both for data exploration and for publication-quality figures.
***

Intro to the Grammar of Graphics

Grammar of graphics: a language to be able to communicate about what we are plotting programatically

(Figure borrowed from Paul Hiemstra)

(Figure borrowed from Paul Hiemstra)

This grammar allows very specific control over your plot and the ability to change features (easily). Plots can be scaled and made pretty enough for publication quality images.

ggplot2 was made to interact well with tidy (long) datasets. I have found that if someone is spending lots of time figuring out how to make a scatterplot, his or her data is probably not in the correct format for ggplot2.

The Grammar of Graphics in Action: Making Figures with ggplot2

Our Dataset

Metagenomic 16SrRNA sequencing of latrines from Tanzania and Vietnam at different depths (multiples of 20cm).

We have 2 csv files (change one to tsv or xlsx - maybe both and make an additional files and get a google spreadsheet): 1. A metadata file (Naming conventions: [Country_LatrineNo_Depth]) with sample names and environmental variables. 2. A table of species abundance.

B Torondel, JHJ Ensink, O Gunvirusdu, UZ Ijaz, J Parkhill, F Abdelahi, V-A Nguyen, S Sudgen, W Gibson, AW Walker, and C Quince. Assessment of the influence of intrinsic environmental and geographical factors on the bacterial ecology of pit latrines Microbial Biotechnology, 9(2):209-223, 2016. DOI:10.1111/1751-7915.12334


Scatterplot Example

I have saved a version of the OTU table in tidy format (which we created in Lesson 2 - split_dat). As well as a modified version of the metadata table.

Let’s read them in and also load ggplot2.

library(tidyverse)

dat <- read.csv("data/long_SPE_pitlatrine.csv", stringsAsFactors = F, header = TRUE)

Let’s build a plot by adding components one by one to see how the grammar of graphics is implemented in ggplot2. To start, we of course need to input our data. However, if that is all we input, what we get back is a blank graphic. We have not yet said what we want to plot and how we want to plot it.

library(tidyverse)
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 2.2.1     ✔ purrr   0.2.4
✔ tibble  1.4.2     ✔ dplyr   0.7.4
✔ tidyr   0.8.0     ✔ stringr 1.3.0
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
dat <- read.csv("data/long_SPE_pitlatrine.csv", stringsAsFactors = F, header = TRUE)

We have, however, created a ggplot object. This is a list of 9 parameters: data, layers, scales, mapping, theme, coordinates, facet, plot environment, and labels. Luckily there are some defaults, so we don’t have to specify everything, but you can start to see how ggplot objects are highly customizable.

ggplot(ndat)

The next step is to choose the data we are plotting (aesthetics). At this point the data can be scaled and the axes appear. We have not yet specified how we want the data plot, just which data should be plotted. In practice, people usually omit ‘mapping =’, but it is a good reminder that mapping is, in fact, what we are doing.

str(ggplot(ndat))
List of 9
 $ data       :'data.frame':    81 obs. of  14 variables:
  ..$ Country       : Factor w/ 2 levels "Tanzania","Vietnam": 1 1 1 1 1 1 1 1 1 1 ...
  ..$ Latrine_Number: int [1:81] 2 2 2 2 2 2 2 2 3 3 ...
  ..$ Depth         : int [1:81] 1 10 12 2 3 6 7 9 2 3 ...
  ..$ pH            : num [1:81] 7.82 9.08 8.84 6.49 6.46 7.69 7.48 7.6 7.55 7.68 ...
  ..$ Temp          : num [1:81] 25.1 24.2 25.1 29.6 27.9 28.7 29.8 25 28.8 28.9 ...
  ..$ TS            : num [1:81] 14.5 37.8 71.1 13.9 29.4 ...
  ..$ VS            : num [1:81] 71.33 31.52 5.94 64.93 26.85 ...
  ..$ VFA           : num [1:81] 71 2 1 3.7 27.5 1.5 1.1 1.1 30.9 24.2 ...
  ..$ CODt          : int [1:81] 874 102 35 389 161 57 107 62 384 372 ...
  ..$ CODs          : int [1:81] 311 9 4 180 35 3 9 8 57 57 ...
  ..$ perCODsbyt    : int [1:81] 36 9 10 46 22 6 8 13 15 15 ...
  ..$ NH4           : num [1:81] 3.3 1.2 0.5 6.2 2.4 0.8 0.7 0.9 21.6 20.4 ...
  ..$ Prot          : num [1:81] 35.4 18.4 0 29.3 19.4 0 14.1 7.6 33.1 44.5 ...
  ..$ Carbo         : num [1:81] 22 43 17 25 31 14 28 28 47 48 ...
 $ layers     : list()
 $ scales     :Classes 'ScalesList', 'ggproto' <ggproto object: Class ScalesList>
    add: function
    clone: function
    find: function
    get_scales: function
    has_scale: function
    input: function
    n: function
    non_position_scales: function
    scales: NULL
    super:  <ggproto object: Class ScalesList> 
 $ mapping    : list()
 $ theme      : list()
 $ coordinates:Classes 'CoordCartesian', 'Coord', 'ggproto' <ggproto object: Class CoordCartesian, Coord>
    aspect: function
    distance: function
    expand: TRUE
    is_linear: function
    labels: function
    limits: list
    range: function
    render_axis_h: function
    render_axis_v: function
    render_bg: function
    render_fg: function
    train: function
    transform: function
    super:  <ggproto object: Class CoordCartesian, Coord> 
 $ facet      :Classes 'FacetNull', 'Facet', 'ggproto' <ggproto object: Class FacetNull, Facet>
    compute_layout: function
    draw_back: function
    draw_front: function
    draw_labels: function
    draw_panels: function
    finish_data: function
    init_scales: function
    map: function
    map_data: function
    params: list
    render_back: function
    render_front: function
    render_panels: function
    setup_data: function
    setup_params: function
    shrink: TRUE
    train: function
    train_positions: function
    train_scales: function
    vars: function
    super:  <ggproto object: Class FacetNull, Facet> 
 $ plot_env   :<environment: R_GlobalEnv> 
 $ labels     : list()
 - attr(*, "class")= chr [1:2] "gg" "ggplot"

We now have chosen the geometric object (geom) with which to plot our data, in this case a point. A geom could be a line, a bar, a boxplot - you can type geom_ and then Tab to see all of the available options. Autocomplete can also be helpful for remembering syntax.

ggplot(ndat, mapping = aes(x = TS, y = CODt))

The data looks like there are two groupings. My guess would be that this is for the 2 different countries. We can easily test this by colouring our points by Country. A legend will be automatically created for you. I have also changed the shape and size of the geom. A quick reference key for shapes can be found in the ‘Cookbook for R’ (http://www.cookbook-r.com/Graphs/Shapes_and_line_types/).

ggplot(ndat, aes(x=TS, y=CODt)) + geom_point() 

Some of our data points seem to be crushed near the x-axis. We can scale the y-axis to fix this. When we start customizing our plot, it easier to read our code if we have each specification on a new line.

ggplot(ndat, aes(x=TS, y=CODt, colour = Country)) + geom_point(shape = 18, size = 5) 

Faceting

Faceting allows us to split our data into groups. Note that I have removed the colour. It is good data visualization practice to only have one attribute (colour, shading, faceting, symbols) per grouping.

ggplot(ndat, aes(x=TS, y=CODt, colour = Country)) + 
  geom_point(alpha = 0.3) + 
  scale_y_log10()

I could now add information from another variable as a colour in this plot. Note that if a variable is continuous instead of discrete, the colour will be a gradient.

ggplot(ndat, aes(x=TS, y=CODt)) + 
  geom_point() +
  scale_y_log10() +
  facet_grid(~Country)

If we really wanted to we could add another variable to the plot by changing the shape attribute. Note that shape can only be used for discrete values. Let’s change Country to having 2 shapes and facet by the Depth of the latrine instead.

ggplot(ndat, aes(x=TS, y=CODt, colour = Temp)) + 
  geom_point() +
  scale_y_log10() +
  facet_grid(~Country)

We can now note that only Tanzania had sampling greater than 4cm of depth. There are single latrines for 4 samples. There was no latrine at a depth of 11cm. Lack of replication and a bias towards Tanzania for the higher depths is something we should keep in mind while looking at this data. Depending on the question we are trying to answer, we may want to remove some of this data. We can also note that our numbers are being ordered as if they were characters. If you recall we created Depth by splitting a sample name. We can change Depth to numeric type, and the numbers will be ordered properly.

ggplot(ndat, aes(x=TS, y=CODt, colour = Temp, shape = Country)) + 
  geom_point() +
  scale_y_log10() +
  facet_wrap(~Depth, scales = "free_y")

One thing that is not necessary in this case - but good to know about - is the ability to allow each grid to have its own independent axis scale. In our example, wells of Depths from 1-4cm have up to 1000 CODt, while the other wells barely have values past 100 CODt. This can be changed, but keep in mind most people will assume all grids have the same scale, so take extra care to point that the scales are different when presenting or publishing.

ndat$Depth <- as.numeric(ndat$Depth)
ggplot(ndat, aes(x=TS, y=CODt, colour = Temp, shape = Country)) + 
  geom_point() +
  scale_y_log10() +
  facet_wrap(~Depth)

Adding Regression Lines

You can also add statistical transformations to your plots. Again, take a look at stat_ then Tab to see the list of options. In this case let’s separately fit a linear regression line to CODt vs TS for each country. The grey area around the line is the confidence interval (default=0.95) and can be removed with the additional call to stat_smooth of ‘se = FALSE’.

ggplot(ndat, aes(x=TS, y=CODt, colour = Temp, shape = Country)) + 
  geom_point() +
  scale_y_log10() +
  facet_wrap(~Depth, scales = "free_y")

A linear model is not necessarily the best fit. The method of calculating the smoothing function can be changed to other provided functions or can be a custom formula. Note that I changed the confidence interval by modifying ‘level=0.8’. geoms can be made more transparent with the alpha parameter, which is set to 0.3 in the following code so that the emphasis is on the regression line rather than the points.

ggplot(ndat, aes(x=TS, y=CODt)) + 
  geom_point() +
  scale_y_log10() +
  facet_grid(~Country) +
  stat_smooth(method = lm)

Exploring different types of plots

Density Plots

We are making a density plot for OTUs by Country. I have set alpha (transparency) to 0.3 so that we can see both countries on our plot.

ggplot(ndat, aes(x=TS, y=CODt)) + 
  geom_point(alpha = 0.3) +
  scale_y_log10() +
  facet_grid(~Country) +
  stat_smooth(method = loess, level = 0.8)

The first thing to notice is that everything is clumped at 0. This is because we have not filtered our data frame to remove all observations where OTUs are zero. Here we filter to have at least 2 OTUs. The other thing to notice is that there is a long tail where there will only be a few observations. It will be necessary to change the x-axis to see our data. Note that R gives us a warning that we are not viewing 158 of our 4212 rows. We can add a rug geom to see each value.

ggplot(dat, aes(x=OTUs, fill=Country, alpha=0.3)) + 
    geom_density() 

Histograms

Histograms instead count the number of observations you have in each ‘bin’ that you specify. The default binwidth is 30. THIS HAS NOTHING TO DO WITH YOUR DATA!! CHANGE IT!! R will even warn you to change your binwidth.

Instead of having the countries information stacked, we may want to see the data side by side. This can be done with the parameter ‘position’ set to “dodge”. A rug geom can also be added to a histogram.

ggplot(dat[dat$OTU >=2,], aes(x=OTUs, fill=Country, alpha=0.3)) + 
    geom_histogram() +
  xlim(0,1000)


Challenge

Using the data filtered for OTUs greater than or equal to two, make a violin plot of the distribution of OTUs for each country. Use a log scale. Colour the violin plots by Country. Draw lines across the violin plot where the quantiles (25th, 50th, 75th) are for each plot. What do the widths of the plots represent?





Bar plots

Let’s create a bar plot of OTUs per country and use the filled in colour to represent Taxa.

ggplot(dat, aes(x=Country, y=OTUs, fill=Taxa)) + 
    geom_bar() 

This is a common error because the default for geom_bar is to use the y-axis for a count. To use it for a variable instead, we have to specify stat=“identity”.

ggplot(dat, aes(x=Country, y=OTUs, fill=Taxa)) + 
    geom_bar() 
Error: stat_count() must not be used with a y aesthetic.

Controlling the order of your categorical variables in your legend

In the legend for the diagram above our factor levels for Taxa are in alphabetical order. However, an ordering that might be more useful would be the order that matches our data, for example, Taxa in descending order of OTUs. To do this, we can use the forcats package. fct_reorder2() takes the factor we want to order by (Taxa) and orders it by the values given (Country, OTUs). We will talk about customizing plots later in this lesson, but the last line of code is one way to remove the legend title (by making the legend title ‘blank’).

Now our highest abundance Taxa is the first value in our legend, and this matches the order of the Taxa in our bar graph. The legend order now has meaning. The white line in the Vietnam bar graph is a Taxa for which there was an OTU value in Tanzania but no data in Vietnam (since anything <=2 was filtered out of the data set).

Let’s take a second to look at what happens when stat does not equal ‘identity’.

What is being counted instead of OTUs? Why does the colouring for Taxa look so different?

You can alternate between stacked or “dodged” for whether your bars are on top of each other or next to each other (split by a factor or categorical variable) or on top.

ggplot(dat, aes(x=Country, fill=Taxa)) + 
    geom_bar() 

You can have your bars horizonally instead of verstically by using coord_flip()l.

ggplot(ndat, aes(x=Depth, y=pH, fill = Country)) + 
    geom_bar(stat="identity", position = "dodge") 

Circular Plots

In ggplot, circular plots are related to bar graphs - they just have different coordinate systems. The default coordinate system is cartesian coordinates, and we need to switch to polar coordinates to make a circle. This is a Coxcomb plot - pH levels increase as you move up the outer rings, and depth increases as you move clockwise around the circle. Colour is still represented by country.

ggplot(ndat, aes(x=Depth, y=pH, fill = Country)) + 
    geom_bar(stat="identity", position = "dodge") +
  coord_flip()

To make your classic venn diagram, use theta to specify what variable is going to be used to make up the angles (width of pie slices). To wrap to a full circle instead of having sections, the width is set to one.

ggplot(ndat, aes(x=Depth, y=pH, fill = Country)) + 
    geom_bar(stat="identity", position = "dodge") +
  coord_polar()

Lines

To draw a line graph, we select geom_line().

ggplot(dat, aes(x="", y=Country, fill = Country)) + 
    geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y")

We can colour the lines for the different countries. Looking at our previous graph, what must have happened when these values were over the same range?

ggplot(ndat, aes(x=Temp, y=pH)) + geom_line() 

Error bars

To plot error bars, we need to give the geom, geom_errorbar(), the interval over which we want to draw the bar. This is usually something like one standard deviation or one standard error or a confidence interval. Luckily, we have learned how to calculate the mean and standard deviation with dplyr. Then, all we need to do is add or subtract the standard deviation from our data point to get the bounds of the error bar. geom_errorbar() takes these bounds passed to the parameters ymax and ymin. In this case, since there were no sample replicates, the standard deviation is taken from the mean of all of the soil readings in a given country at a given depth.

ggplot(ndat, aes(x=Temp, y=pH, colour = Country)) + geom_line() 

Stripplots

The first plots we made we scatterplots with 2 continuous variables. With one discrete variable and one categorical variable, we can make a stripplot.

errdat <- ndat %>% group_by(Country, Depth) %>% mutate(mean_pH = mean(pH)) %>% mutate(sd_pH= sd(pH))
ggplot(errdat, aes(x=Temp, y=pH, colour = Country)) + 
  geom_line() + 
  geom_errorbar(aes(ymin=pH-sd_pH, ymax = pH+sd_pH), width = .2, alpha = 0.4)

Again, we have a lot of values crushed near the x-axis. It we add log scaling to the y-axis, we get an error that this transformation created infinite values. This is because we have zeros in our data set. Knowing this, we could ignore the warning, or add +1 to each OTU.

ggplot(dat, aes(x = Depth, y = OTUs)) + geom_point() 

ggplot(dat, aes(x = Depth, y = OTUs)) + geom_point() + scale_y_log10() 

In order to see the points a little better, we can ‘jitter’ them.

ggplot(dat, aes(x = Depth, y = (OTUs+1))) + geom_point() + scale_y_log10() 

Bubble Plots + Labels

Let’s group our OTUs by Country and Taxa and look at the Taxa for the top 30 OTUs.

With bubble plots, we need to provide the size of our bubbles in a way that is proportional to our data, and provide a scale to map these to.

We are going to make our points with geom_jitter so that our bubbles don’t overlap. Remember that we want to specify what we are plotting with aesthetics. We want the bubbles representing OTUs_per_Taxa, so we divide each value by pi when calling size since bubbles are circles.

ggplot(dat, aes(x = Depth, y = (OTUs+1))) + geom_point(position = "jitter") + scale_y_log10() + facet_wrap(~Country)

However, it is hard to tell proportionally how different these sizes are. Mapping values to a scale gives us more of an idea of their relative sizes.

bdat <- dat %>% select(-Latrine_Number, -Depth) %>% group_by(Taxa, Country) %>% mutate(OTUs_per_Taxa = sum(OTUs)) %>% select(-OTUs)
bdat <- unique(bdat) %>% arrange(desc(OTUs_per_Taxa)) %>% .[1:30,]
ggplot(bdat, aes(x = Country, y = OTUs_per_Taxa, fill=Taxa)) +
  scale_y_log10() +
  geom_jitter(aes(size = OTUs_per_Taxa/pi), pch = 21, show.legend = TRUE)  +
  guides(fill = FALSE)

ggplot2 provides 2 methods of labelling. Let’s look at the help menu to find out what the difference between the 2 of them is. We have to specify in with aesthetics, that we are plotting our label.

ggplot(bdat, aes(x = Country, y = OTUs_per_Taxa, fill=Taxa)) +
  scale_y_log10() +
  geom_jitter(aes(size = OTUs_per_Taxa/pi), pch = 21, show.legend = FALSE) + 
  scale_size_continuous(range=c(1,30)) +
  guides(fill=FALSE)

This isn’t fantastic because our labels are overlapping. What parameters might you be able to use to move the labels? Try using them to get the labels to not overlap.

geom_text has an option to check for overlap of labels. geom_text does not provide a background for the label, and it may be harder to tell which label belongs to each data point.

ggplot(bdat, aes(x = Country, y = OTUs_per_Taxa, fill=Taxa)) +
  scale_y_log10() +
  geom_jitter(aes(size = OTUs_per_Taxa/pi), pch = 21, show.legend = FALSE) + 
  scale_size_continuous(range=c(1,30)) +
  geom_label(aes(label = Taxa), show.legend = FALSE)

What happened here? Why don’t we have as many labels? Look in the help menu to explain this behaviour.

I don’t like futzing with label positions, so I went looking for a package that would do this for me. ggrepel() will ‘repel’ your labels away from each other without getting rid of them. Let’s check it out with our bubble plot labels. Install and load ggrepel. The equivalent function we can use is geom_label_repel(). The force parameter allow you to modify how far you want your labels pushed away from each other. Here, colour is being used to map to our data points but ggrepel can ditch the box and connect a line to data points instead. Variations on use can be found here: https://cran.r-project.org/web/packages/ggrepel/vignettes/ggrepel.html.

ggplot(bdat, aes(x = Country, y = OTUs_per_Taxa, fill=Taxa)) +
  scale_y_log10() +
  geom_jitter(aes(size = sqrt(OTUs_per_Taxa/pi)), pch = 21, show.legend = FALSE) + 
  scale_size_continuous(range=c(1,30)) +
  geom_text(aes(label = Taxa), check_overlap = TRUE, show.legend = FALSE)

Boxplots

Boxplots are a great way to visualize summary statistics for your data. As a reminder, the thick line in the center of the box is the median. The upper and lower ends of your box are the first and third quartiles or 25th and 75th percentiles of your data. The whiskers extend to the largest value no further than 1.5*IQR (inter-quartile range - the distance between the first and third quartiles). Data beyond these whiskers are considered outliers and plotted as individual points. This is a quick way to see how comparable your samples or variables are.

We are going to see boxplots for the distribution of OTUs per Taxa across all samples.

library(ggrepel)
ggplot(bdat, aes(x = Country, y = OTUs_per_Taxa, fill=Taxa)) +
  scale_y_log10() +
  geom_jitter(aes(size = sqrt(OTUs_per_Taxa/pi)), pch = 21, show.legend = FALSE) + 
  scale_size_continuous(range=c(1,30)) +
  geom_label_repel(aes(label = Taxa), force = 2,show.legend = FALSE, segment.size=0)

While it is clear that Clostridia is the most represented taxa, it is difficult to tell whether some other taxa have no representation, or if they are lowly represented. Transforming to a log scale on the y-axis will sort this out for us.

ggplot(dat, aes(x = Taxa, y = OTUs)) + geom_boxplot() + theme(axis.text.x = element_text(angle=90, hjust=1))

We could also clean this plot up a bit by removing those taxa with 1 or fewer OTUs. And have the condition that it must occur in more than 1 sample.

ggplot(dat, aes(x = Taxa, y = OTUs)) + geom_boxplot() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_y_log10()

If we facet by country we can see that Acidobacteria_Gp1, for example, was only found in Vietnam and not Tanzania.

keep <- dat %>% ungroup() %>% filter(OTUs>=2) %>% group_by(Taxa) %>% summarize(n = n()) %>% filter(n > 1) %>% select(Taxa)
ggplot(dat[dat$Taxa %in% keep$Taxa,] , aes(x = Taxa, y = OTUs)) + geom_boxplot() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_y_log10()

For now, let’s just keep the Taxa that are common to both countries.

ggplot(dat[dat$Taxa %in% keep$Taxa,] , aes(x = Taxa, y = OTUs)) + 
  geom_boxplot() + 
  theme(axis.text.x = element_text(angle=90, hjust=1)) + 
  scale_y_log10() + facet_grid(~Country)

Beeswarm Plots

Even though boxplots give us summary statistics on our data, it is useful to be able to see where our data points are. Adding the data using geom_point() adds our data in the form of a stripplot to our boxplots. It would be nice to see how many points we have at each OTU value, but there are a lot of points overlapping here.

keep <- dat %>% 
  ungroup() %>% 
  filter(OTUs>=2) %>% 
  group_by(Country, Taxa) %>% 
  summarize(n = n()) %>% 
  filter(n > 1) 
keep$dup <- keep %>% ungroup() %>% select(Taxa) %>% duplicated(.)
keep <- keep[keep$dup, "Taxa"]
ggplot(dat[dat$Taxa %in% keep$Taxa,] , aes(x = Taxa, y = OTUs)) + geom_boxplot() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_y_log10() + facet_grid(~Country)

Jittering the points gives a general idea, but is a bit too widely dispursed to give a good sense of the data.

ggplot(dat[(dat$Taxa=="Clostridia" | dat$Taxa == "Gammaproteobacteria" | dat$Taxa == "Unknown" | dat$Taxa == "Bacilli") & dat$Country == "T",] , aes(x = Taxa, y = OTUs)) + 
  geom_boxplot() + 
  geom_point() +
  theme(axis.text.x = element_text(angle=90, hjust=1)) + 
  scale_y_log10()

A beeswarm plot places data points that were overlapping instead next to each other, so we can get a better picture of the distribution of points. We simply overlay the points with geom_beeswarm().

ggplot(dat[(dat$Taxa=="Clostridia" | dat$Taxa == "Gammaproteobacteria" | dat$Taxa == "Unknown" | dat$Taxa == "Bacilli") & dat$Country == "T",] , aes(x = Taxa, y = OTUs)) + 
  geom_boxplot() + 
  geom_jitter() +
  theme(axis.text.x = element_text(angle=90, hjust=1)) + 
  scale_y_log10()

Increasing the spacing between data points can make this distribution a bit clearer.

library(ggbeeswarm)
ggplot(dat[(dat$Taxa=="Clostridia" | dat$Taxa == "Gammaproteobacteria" | dat$Taxa == "Unknown" | dat$Taxa == "Bacilli") & dat$Country == "T",] , aes(x = Taxa, y = OTUs)) + 
  geom_boxplot() + 
  geom_beeswarm() +
  theme(axis.text.x = element_text(angle=90, hjust=1)) + 
  scale_y_log10()

Using geom_quasirandom() gives the empirical distribution of the stripplot to avoid overplotting.

ggplot(dat[(dat$Taxa=="Clostridia" | dat$Taxa == "Gammaproteobacteria" | dat$Taxa == "Unknown" | dat$Taxa == "Bacilli") & dat$Country == "T",] , aes(x = Taxa, y = OTUs)) + 
  geom_boxplot() + 
  geom_beeswarm(cex = 2.2) +
  theme(axis.text.x = element_text(angle=90, hjust=1)) + 
  scale_y_log10()

Other spacing and distribution options are available at https://github.com/eclarke/ggbeeswarm.

A Note on Colour Palettes

There are 3 main types of colour palettes. 1. Sequential - implies an order to your data ie. light to dark implies low values to high values. 2. Diverging - low and high values are extremes, and the middle values are important still goes from light to dark, but 3 colour mainly used. 3. Qualitative - there is no quantitative relationship between colours. This is usually used for categorical data.

Which of these is ggplot2’s default color palette?

Many colour palettes now exist. I showcase a couple that work nicely with ggplot2. These packages also have colorblind friendly options. RColorBrew has options for these 3 types of palettes, which you can see with display.brewer.all(). With a smaller dataset, we could make a call to scale_fill_brewer(), which just requires a choice of one of RColorBrewer’s palettes, such as “Spectral”. However, we have 24 Taxa and these palettes have 8-12 colours, so we have to get creative. I have simply taken the 2 qualitative palettes that each have a length of 12, put them into one palette, and made sure my values were unique. This can then be passed to ggplot via scale_fill_manual(). You can always choose a vector of your own colors using this ‘R color cheatsheet’(https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf).

labels <- c(T = "Tanzania", V = "Vietnam")
p <- ggplot(dat[dat$Taxa %in% keep$Taxa,] , aes(x = Taxa, y = OTUs, fill = Taxa)) + 
  geom_boxplot(outlier.colour = "red") + 
  scale_y_log10() + 
  facet_grid(~Country, labeller = labeller(Country = labels)) +
  ggtitle("Abundance of Taxa by Country") +
  xlab(NULL) +
  ylab("log(OTUs)") + 
  guides(fill=FALSE)
p

The viridis package also has some nice color palettes (https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html). I think they might all be diverging palettes (qualitative is best for our Taxa), but I will showcase a couple here.

library(RColorBrewer)
library(viridis)
Loading required package: viridisLite
display.brewer.all()

#p + scale_fill_brewer(palette = "Spectral")
#you can also use the names of colours
#scale_fill_manual(values=c("purple", "cornflowerblue", "grey", "yellow", "orange", "brown"))
palette1 <- brewer.pal(12, "Paired")
palette2 <- brewer.pal(12, "Set3")
length(unique(c(palette, palette2)))
[1] 13
custom <- c(palette1, palette2)
p + scale_fill_manual(values = custom)

p + scale_fill_viridis(discrete = TRUE)

p + scale_fill_viridis(discrete = TRUE, option = "plasma")

RSkittleBrewer is another option for funky colour palettes. ggsci has a variety of color palettes inspired by different scientific journals as well as television shows (https://cran.r-project.org/web/packages/ggsci/vignettes/ggsci.html).

Customization of Theme

As mentioned earlier, it is possible to customize every single aspect of a ggplot. Most of this occurs with a call to theme, which you can think of as modifying everything BUT your data. For example, my axis labels can be modified, but they (hopefully) have something to do with my data. However, changing the size of the text or the font of the labels is unrelated to my data, and that same structure could be carried over to other plots if I saved my own theme.

Things that you can change with theme include the axis, legend, panels, gridlines, or background.

Each element of theme inherits from one of:

ggplot comes with some themes - so I would suggest starting with the one that is closest to what you want, and start modifying from there.

Check out these themes: - theme_minimal() - theme_classic() - theme_bw() - theme_void() - theme_dark() - theme_gray() - theme_light()

You can look at the default for each theme simply by typing it into the console.

library(viridis)
p + scale_fill_viridis(discrete = TRUE)

p + scale_fill_viridis(discrete = TRUE, option = "plasma")

And this is what theme_bw() looks like:

theme_bw
function (base_size = 11, base_family = "") 
{
    theme_grey(base_size = base_size, base_family = base_family) %+replace% 
        theme(panel.background = element_rect(fill = "white", 
            colour = NA), panel.border = element_rect(fill = NA, 
            colour = "grey20"), panel.grid.major = element_line(colour = "grey92"), 
            panel.grid.minor = element_line(colour = "grey92", 
                size = 0.25), strip.background = element_rect(fill = "grey85", 
                colour = "grey20"), legend.key = element_rect(fill = "white", 
                colour = NA), complete = TRUE)
}
<environment: namespace:ggplot2>

Here is an example of theme_dark.

p + theme_bw()

There are also themes that have been created and put into a package, ggthemes. Some of these themes are based off of graphs seen in print or on websites (the economist, wall street journal, fivethirtyeight) or to match standard tools (excel, google docs). Information about the themes can be found at https://github.com/jrnold/ggthemes.

p + theme_dark()

library(ggthemes)
p + theme_economist()

You can also make your own custom theme as demoed here: http://joeystanley.com/blog/custom-themes-in-ggplot2

I am going to show you how to modify a plot, starting from theme_minimal() because I don’t like the grey backgrounds or harsh axis lines.

p + theme_stata()

Things I don’t like about this plot and their solutions:

|Problem | Solution | |_________________________________________________________:|:__________________________________________________________________________:| |x-axis labels overlap | rotate lables: axis.text.x = element_text(angle =90, hjust=1) | |country labels are smaller than axis labels | change size and face: strip.text.x = element_text(face = “bold”, size = 16)| |title is uncentered | adjust horizontally: plot.title = element_text(hjust=0.5, size = 18) | |border to separate countries | create a border: panel.border = element_rect(fill = NA) | |make y axis ticks | create y axis ticks: axis.ticks.y = element_line() |

It isn’t necessary to remember all of this syntax, I frequently find myself back at the help page at http://ggplot2.tidyverse.org/reference/theme.html.

The last call to theme will override previous calls. Therefore, if we want to start with theme_minimal() as our base, it has to be in our code BEFORE the other modifications.

p + theme_minimal()


Challenge

Change the colour of the plot background. Add minor gridlines, and make the major gridlines black. Increase the spacing in between the panels.





Correlation Plots and Heatmaps

One common way to look for outliers or trends in data is to calculate the correlation between samples and make a correlation plot. Since samples are expected to be in columns, this is an example of where you would use ‘wide’ data. The default correlation coefficient is the Pearson correlation, but others (Kendall, Spearman) can be specified. Note that the entire matrix must be numeric.

A heatmap can be made in ggplot by using the tile geom. The fill is now the correlation coefficient, which is a continuous variable (hence the gradient). I have changed the title of the legend from ‘value’ to ‘Pearson Correlation’ by using scale_fill_continuous(). The ‘’ included in the name is a line break. A fantastic resource for modifying legends is http://www.cookbook-r.com/Graphs/Legends_(ggplot2)/.

ggplot(dat[dat$Taxa %in% keep$Taxa,] , aes(x = Taxa, y = OTUs, fill = Taxa)) + 
  geom_boxplot(outlier.colour = "red") + 
  theme_minimal() +
  theme(axis.text.x = element_text(angle =90, hjust=1), 
        panel.border = element_rect(fill = NA), 
        strip.text.x = element_text(face = "bold", size = 16), 
        plot.title = element_text(hjust=0.5, size = 18), 
        axis.ticks.y = element_line()) + 
  scale_y_log10() + 
  facet_grid(~Country, labeller = labeller(Country = labels)) +
  ggtitle("Abundance of Taxa by Country") +
  xlab(NULL) +
  ylab("log(OTUs)") + 
  guides(fill=FALSE)

cdat <- read.csv("data/ENV_pitlatrine.csv", row.names = 1)
cdat <- cor(cdat)
library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths

Error in names(frame) <- `*vtmp*` : names() applied to a non-vector
melt <-melt(cdat)
ggplot(melt, aes(Var1, Var2, fill = value)) + 
  geom_tile() +
  xlab(NULL) +
  ylab(NULL) +
  scale_fill_continuous(name = "Pearson\nCorrelation")

If this looks a little jarring to you compared to ‘normal’ heatmaps, it is probably because the data has not been ordered yet. This can be done by calculating the distance between samples (ie. euclidean) and them clustering them in a hierarchical way (ie. complete linkage).

To have a different colour scheme that for example, goes from red to yellow, you can use scale_fill_gradient() and specify what you want the diverging colours to be. Note that your legend title now derives from this gradient.

library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths

We can now see that there is a patch of dark red samples with low correlation. It is difficult to see which sites these are as the names are a bit small.

Enter the interactive heatmap! Plotly allows for interactive plots in a variety of languages, and allows the creation of sharable links (if you are interested you have to sign up for a free account to get API credentials https://plot.ly/r/getting-started/). They have created a package, which, luckily for us, interfaces seamlessly with ggplot2. I only need to save my plot and call the function ggplotly() to have an interactive graph!

The response of plotly to manipulations other than hovering can be a bit slow if not using the API and may freeze/crash R, so I recommend saving your code before trying it.

cdat <- cdat[hc$order, hc$order]
Error: object 'hc' not found

Hovering will highlight the variables and values relating to that specific data point, and areas can be zoomed in on. We can now see the names of the low correlation samples. While they are all from Tanzania, they do not appear to all be from the same Latrine_Number or Depth.

ggvis is a Bioconductor package allowing interactive graphics in R (http://ggvis.rstudio.com/). It is founded on the grammar of graphics and the syntax is similar, but not identical to ggplot2. It is good for interactive scatterplots, bar graphs, line graphs and histograms (although not heatmaps at this point in time). The nature of the interactiveness is highly customizable: you can hover, add slider bars, checkboxes or radiobuttons (it uses Shiny’s reactive programming).

In this example I have just taken a scatterplot that we made earlier, and made it possible to hover over a point and see its underlying data. Don’t worry about the function and syntax too much - those will be introduced in later lessons. I just wanted to show you another option for interactive visualization that hopefully isn’t too intimidating.

library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Saving in ggplot2

ggplot2 has its own function for saving ggsave().

You can send the plot object to the graphics device and then save that image.

With ggsave you can minimally input the filename you would like to have, and the path to your file (you can instead combine the path and filename in the filename).

library(ggvis)

Attaching package: ‘ggvis’

The following objects are masked from ‘package:plotly’:

    add_data, hide_legend

The following object is masked from ‘package:ggplot2’:

    resolution
p <- ggvis(ndat, x= ~TS, y= ~CODt) 
  
layer_points(p) %>% add_tooltip(function(x) c(paste0("TS=", x$TS, ", CODT=", x$CODt)))
Loading required package: shiny
Showing dynamic visualisation. Press Escape/Ctrl + C to stop.

However, in some cases you want to tailor your output. You can specify the width, height and units of your image, or you can apply a scaling factor. You can also specify the plot object you want to save instead of whatever was on your graphics device last using the ‘plot’ parameter. ggsave() can save in a variety of file extensions including pdf, jpeg, tiff, bmp, svg or png.

Taking it up a notch

Multiple plots on one page (ie. for publication images)

There are a variety of methods to mix multiple graphs on the same page, however ggplot does not work well with all of them. I am going to work with a package based that uses gridExtra(which allows us to arrange plots) but works well with ggplot called ggpubr (which allows us to align the axes of our plots). For a demonstration, we are going to take 3 plots that we made earlier (a boxplot, a histogram, and a bubble plot), save them as objects, and then arrange and align them in the same figure. (http://www.sthda.com/english/rpkgs/ggpubr/)

There is a function ggarrange() which takes your plots, their labels, and how they are arranged in rows and columns. To start, if we just have our boxplot and bubble plot, and we wanted them to be side by side, if you picture each plot as a square in a grid, we would have two columns ncol = 2 and 1 row

While our grid areas are the same size, our plot backgrounds are not. Let’s adjust the legend so that it is in the top right corner of the plot, and get remove the white background. We do not need the alpha legend. Do you remember how to get rid of it?

ggarrange(bub, hist, 
          labels = c("A", "B"),
          ncol = 2, nrow = 1)
Removed 158 rows containing non-finite values (stat_bin).Removed 3 rows containing missing values (geom_bar).

Imagine a square with 4 boxes. We are going to put our boxplot in the left top and bottom boxes, the histogram in the top right box, and the bubble plot in the bottom right box. To do this, we are arranging 2 columns (one with the boxplot and one with the histogram + boxplot) and we are arranging 2 rows (one with the histogram and one with the boxplot).

ggarrange(bub, hist, 
          labels = c("A", "B"),
          ncol = 2, nrow = 1)
Removed 158 rows containing non-finite values (stat_bin).Removed 3 rows containing missing values (geom_bar).

The y-axis lines for the histogram and boxplot are not aligned, but this can be fixed with a call to align="v".

ggarrange(box, ggarrange(hist, bub, 
          labels = c("B", "C"),
          nrow = 2),
           ncol = 2, labels = "A")
Removed 158 rows containing non-finite values (stat_bin).Removed 3 rows containing missing values (geom_bar).Transformation introduced infinite values in continuous y-axisTransformation introduced infinite values in continuous y-axisRemoved 7 rows containing non-finite values (stat_boxplot).

To make sure all axis titles are the same size (B and C look a little off to me), we can specify the font we want changed and the size we want to change it to. I am also going to make the legend title size the same. I want to make the bubble plot font a little smaller as well. Let’s look at the font function. Can I do that? What might be another option?

ggarrange(box, ggarrange(hist, bub, 
          labels = c("B", "C"),
          nrow = 2, align = "v"),
           ncol = 2, labels = "A")
Removed 158 rows containing non-finite values (stat_bin).Removed 3 rows containing missing values (geom_bar).Transformation introduced infinite values in continuous y-axisTransformation introduced infinite values in continuous y-axisRemoved 7 rows containing non-finite values (stat_boxplot).


Challenge

Using the code available here for a line graph we made earlier in the lesson, make a combined figure. Imagine a square with 4 boxes. We are going to put our line graph in the top right and left boxes, the histogram in the bottom left box, and the bubble plot in the bottom right box. Make sure the legend and axis titles are the same size. Change the legend text for the line graph to be smaller than the legend title.

lin <- ggplot(errdat, aes(x=Temp, y=pH, colour = Country)) + 
  geom_line() + 
  geom_errorbar(aes(ymin=pH-sd_pH, ymax = pH+sd_pH), width = .2, alpha = 0.4)





There are many fantastic R packages to analyze and visualize your data. As a group, we are likely working on a variety of specialized areas. The plots we have made so far today should be useful for data exploration for many different kinds of data. In the next section we are going to preview some more complex visualization types, but since these take more time to go through and not everyone may be interested in network diagrams, time-series analysis, or geospatial data, we will not be plotting all of these ourselves. If you are interested in a tutorial on one of these visualization types, please indicate that in the comments area of the lesson survey (https://www.surveymonkey.com/r/VNQZ3KS).

We will, however do an Upset Plot as my personal crusade to have something better than venn diagrams in publications.

Interactive graphics

Plotly: - https://plot.ly/r/

ggvis: - http://ggvis.rstudio.com/interactivity.html

Heatmaps: - https://github.com/talgalili/heatmaply

Interactive time-series data: - https://rstudio.github.io/dygraphs/ ###Network diagrams visNetwork (based on igraph) https://datastorm-open.github.io/visNetwork/edges.html

Upset Plots

Upset plots are an alternative to venn diagrams that show the intersection of sets, as well as the size of the sets. Additionally, venn diagrams can be difficult to interpret for greater than 2 or 3 sets.

Let’s see how this works practically. The question that we are asking is how many sites are each Taxa represented at and what is the overlap with other Taxa? The data that we have is an OTU table. For this purpose we simply want to know whether a given Taxa was present or absent in the sample, and then we can form intersections based on this information. So, for each OTU value that is not 0, we replace it with a 1 instead. Then all we have to do is to enter our data set, the number of sets we are inputting, if we want to order the results (in this case by frequency), and how many intersections we want to show. I have picked 8 different Taxa, which would make a crazy venn diagram with a lot of zeros in it since a couple of these Taxa are rare. Here, I will show 15 intersections - we know the remaining intersections would be zero since this is ordered by frequency.

ggarrange(lin+ font("axis.title", size=9) + font("legend.title", size=9) + font("legend.text", size = 8), ggarrange(hist + font("axis.title", size=9) + font("legend.title", size=9), bub + font("axis.title", size=9), 
          labels = c("B", "C"),
          ncol = 2, align = "h"),
           nrow = 2, labels = "A")
Removed 158 rows containing non-finite values (stat_bin).Removed 3 rows containing missing values (geom_bar).Removed 4 rows containing missing values (geom_errorbar).

Let’s look at our greatest Intersection Size (equal to 28). This means that 28 of our 81 samples have Clostridia, Alphaproteobacteria, and Deinococci present in the same sample WITHOUT Chloroflexi, both Acidobacteria, Cyanobacteria or Lentisphaeria also being present. We can see from the Set Size that Clostridia and Alphaproteobacteria are present in almost all samples, and Deinococci is present in greater than 3/4 of the samples. You might expect this overlap to be higher, but remember that this is without the other Taxa and moving along our interesection sizes and dot matrix, we can see that other intersections include these bacteria. Some quick mental math shows that 63 samples have these 3 bacteria present.

Genomics Data

ggbio: - http://www.bioconductor.org/packages/2.11/bioc/vignettes/ggbio/inst/doc/ggbio.pdf

GenVisR: - https://bioconductor.org/packages/release/bioc/vignettes/GenVisR/inst/doc/GenVisR_intro.html

GenomeGraphs: - https://bmcbioinformatics.biomedcentral.com/articles/10.1186/1471-2105-10-2


Challenge

Install and load the gapminder package. Save the gapminder data into an object using dat <- gapminder. Plot the following:

  1. a qualitative variable with a quantitative variable (at least 2 ways).
    • Facet your plot by the qualitative variable.
  2. two quantitative variables (at least 2 ways).
    • Add a regression line to your plot.





Resources:

Wickham, Hadley. (2010). A Layered Grammar of Graphics. Journal of Computational and Statistical Graphics.
Wilkinson, L. (2005), The Grammar of Graphics (2nd ed.). Statistics and Computing, New York: Springer. [14, 18]
http://www.cookbook-r.com/Graphs/
https://github.com/jennybc/ggplot2-tutorial
Tufte, Edward R. The Visual Display of Quantitative Information.
http://stcorp.nl/R_course/tutorial_ggplot2.html
http://ggplot2.tidyverse.org/reference/theme.html http://joeystanley.com/blog/custom-themes-in-ggplot2 https://github.com/jrnold/ggthemes https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf https://cran.r-project.org/web/packages/ggrepel/vignettes/ggrepel.html http://www.cookbook-r.com/Graphs/Legends_(ggplot2)/ https://github.com/eclarke/ggbeeswarm https://cran.r-project.org/web/packages/ggsci/vignettes/ggsci.html http://www.sthda.com/english/rpkgs/ggpubr/ https://rpubs.com/drsong/9575 http://elpub.bib.uni-wuppertal.de/edocs/dokumente/fbb/wirtschaftswissenschaft/sdp/sdp15/sdp15006.pdf http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/81-ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/

Post-Lesson Assessment


Your feedback is essential to help the next cohort of trainees. Please take a minute to complete the following short survey: https://www.surveymonkey.com/r/VNQZ3KS




Thanks for coming!!!

LS0tCnRpdGxlOiAiTGVzc29uIDMgLSBQbG90IGFsbCB0aGUgdGhpbmdzISBGcm9tIERhdGEgRXhwbG9yYXRpb24gdG8gUHVibGljYXRpb24tUXVhbGl0eSBGaWd1cmVzIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgICBrZWVwX21kOiB5ZXMKICAgICAgICAgIHRvYzogVFJVRQogICAgICAgICAgdG9jX2RlcHRoOiA0CiAgaHRtbF9ub3RlYm9vazoKICAgICAgICAgIHRvYzogVFJVRQogICAgICAgICAgdG9jX2RlcHRoOiA0Ci0tLQoqKioKPC9icj4KCiMjQSBxdWljayBpbnRybyB0byB0aGUgaW50cm8gdG8gUiBMZXNzb24gU2VyaWVzCgo8L2JyPgoKVGhpcyAnSW50cm8gdG8gUiBMZXNzb24gU2VyaWVzJyBpcyBicm91Z2h0IHRvIHlvdSBieSB0aGUgQ2VudHJlIGZvciB0aGUgQW5hbHlzaXMgb2YgR2Vub21lIEV2b2x1dGlvbiAmIEZ1bmN0aW9uJ3MgKENBR0VGKSBiaW9pbmZvcm1hdGljcyB0cmFpbmluZyBpbml0aWF0aXZlLiBUaGlzIGNvdXJzZSB3YXMgZGV2ZWxvcGVkIGJhc2VkIG9uIGZlZWRiYWNrIG9uIHRoZSBuZWVkcyBhbmQgaW50ZXJlc3RzIG9mIHRoZSBEZXBhcnRtZW50IG9mIENlbGwgJiBTeXN0ZW1zIEJpb2xvZ3kgYW5kIHRoZSBEZXBhcnRtZW50IG9mIEVjb2xvZ3kgYW5kIEV2b2x1dGlvbmFyeSBCaW9sb2d5LiAKCgoKVGhpcyBsZXNzb24gaXMgdGhlIHRoaXJkIGluIGEgNi1wYXJ0IHNlcmllcy4gVGhlIGlkZWEgaXMgdGhhdCBhdCB0aGUgZW5kIG9mIHRoZSBzZXJpZXMsIHlvdSB3aWxsIGJlIGFibGUgdG8gaW1wb3J0IGFuZCBtYW5pcHVsYXRlIHlvdXIgZGF0YSwgbWFrZSBleHBsb3JhdG9yeSBwbG90cywgcGVyZm9ybSBzb21lIGJhc2ljIHN0YXRpc3RpY2FsIHRlc3RzLCB0ZXN0IGEgcmVncmVzc2lvbiBtb2RlbCwgYW5kIG1ha2Ugc29tZSBldmVuIHByZXR0aWVyIHBsb3RzIGFuZCBkb2N1bWVudHMgdG8gc2hhcmUgeW91ciByZXN1bHRzLiAKCgohW10oaW1nL2RhdGEtc2NpZW5jZS1leHBsb3JlLnBuZykKCjwvYnI+CgpIb3cgZG8gd2UgZ2V0IHRoZXJlPyBUb2RheSB3ZSBhcmUgZ29pbmcgdG8gYmUgbGVhcm5pbmcgaG93IHRvIG1ha2UgYWxsIHNvcnRzIG9mIHBsb3RzIC0gZnJvbSBzaW1wbGUgZGF0YSBleHBsb3JhdGlvbiB0byBpbnRlcmFjdGl2ZSBwbG90cy5UaGUgbmV4dCBsZXNzb24gd2lsbCBiZSBkYXRhIGNsZWFuaW5nIGFuZCBzdHJpbmcgbWFuaXB1bGF0aW9uOyB0aGlzIGlzIHJlYWxseSB0aGUgYmF0dGxlZ3JvdW5kIG9mIGNvZGluZyAtIGdldHRpbmcgeW91ciBkYXRhIGludG8gdGhlIGZvcm1hdCB3aGVyZSB5b3UgY2FuIGFuYWx5c2UgaXQuIFRoZW4gd2Ugd2lsbCBsZWFybiBob3cgdG8gZG8gdC10ZXN0cyBhbmQgcGVyZnJvbSByZWdyZXNzaW9uIGFuZCBtb2RlbGluZyBpbiBSLiBBbmQgbGFzdGx5LCB3ZSB3aWxsIGxlYXJuIHRvIHdyaXRlIHNvbWUgZnVuY3Rpb25zLCB3aGljaCByZWFsbHkgY2FuIHNhdmUgeW91IHRpbWUgYW5kIGhlbHAgc2NhbGUgdXAgeW91ciBhbmFseXNlcy4KCgohW10oaW1nL3Nwb3RpZnktaG93dG9idWlsZG12cC5naWYpCgo8L2JyPgoKVGhlIHN0cnVjdHVyZSBvZiB0aGUgY2xhc3MgaXMgYSBjb2RlLWFsb25nIHN0eWxlLiBJdCBpcyBoYW5kcyBvbi4gVGhlIGxlY3R1cmUgQU5EIGNvZGUgd2UgYXJlIGdvaW5nIHRocm91Z2ggYXJlIGF2YWlsYWJsZSBvbiBHaXRIdWIgZm9yIGRvd25sb2FkIGF0IGh0dHBzOi8vZ2l0aHViLmNvbS9lYWN0b24vQ0FHRUYgX18oTm90ZTogcmVwbyBpcyBwcml2YXRlIHVudGlsIGFwcHJvdmVkKV9fLCBzbyB5b3UgY2FuIHNwZW5kIHRoZSB0aW1lIGNvZGluZyBhbmQgbm90IHRha2luZyBub3Rlcy4gQXMgd2UgZ28gYWxvbmcsIHRoZXJlIHdpbGwgYmUgc29tZSBjaGFsbGVuZ2UgcXVlc3Rpb25zIGFuZCBtdWx0aXBsZSBjaG9pY2UgcXVlc3Rpb25zIG9uIFNvY3JhdGl2ZS4gQXQgdGhlIGVuZCBvZiB0aGUgY2xhc3MgaWYgeW91IGNvdWxkIHBsZWFzZSBmaWxsIG91dCBhIHBvc3QtbGVzc29uIHN1cnZleSAoaHR0cHM6Ly93d3cuc3VydmV5bW9ua2V5LmNvbS9yL1ZOUVozS1MpLCBpdCB3aWxsIGhlbHAgbWUgZnVydGhlciBkZXZlbG9wIHRoaXMgY291cnNlIGFuZCB3b3VsZCBiZSBncmVhdGx5IGFwcHJlY2lhdGVkLiAKCjwvYnI+CgoKKioqCl9fT2JqZWN0aXZlOl9fIEF0IHRoZSBlbmQgb2YgdGhpcyBzZXNzaW9uIHlvdSB3aWxsIGJlIGFibGUgdG8gdXNlIGdncGxvdDIoKSB0byBtYWtlIGEgdG9uIG9mIGRpZmZlcmVudCB0eXBlcyBvZiBwbG90cyB3aXRoIHlvdXIgZGF0YSBmb3IgYm90aCBmb3IgZGF0YSBleHBsb3JhdGlvbiBhbmQgZm9yIHB1YmxpY2F0aW9uLXF1YWxpdHkgZmlndXJlcy4gIAoqKioKCiMjSW50cm8gdG8gdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MKCioqR3JhbW1hciBvZiBncmFwaGljczoqKiBhIGxhbmd1YWdlIHRvIGJlIGFibGUgdG8gY29tbXVuaWNhdGUgYWJvdXQgd2hhdCB3ZSBhcmUgcGxvdHRpbmcgcHJvZ3JhbWF0aWNhbGx5IAoKIVsoRmlndXJlIGJvcnJvd2VkIGZyb20gUGF1bCBIaWVtc3RyYSldKGltZy9nZ3Bsb3QyLnBuZykKCiogVGhlICphZXN0aGV0aWNzKiBhcmUgd2hhdCB5b3UgY2FuIHNlZS4gRm9yIGV4YW1wbGUsIHRoZSBkYXRhIGJlaW5nIHBsb3R0ZWQgb3IgcmVwcmVzZW50ZWQgYnkgYSBzaGFwZSBvciBjb2xvdXIuClRoaXMgZGF0YSBjb3VsZCBiZSBwcmVzZW50ZWQgaW4gbXVsdGlwbGUgd2F5cy4gRGF0YSBpcyBtYXBwZWQgdG8gYWVzdGhldGljcy4gQSBwbG90IGNhbiBoYXZlIG11bHRpcGxlIGxheWVycyAoZm9yIGV4YW1wbGUsCmEgc2NhdHRlcnBsb3Qgd2l0aCBhIHJlZ3Jlc3Npb24gbGluZSkuCgoqIExpbmVzLCBiYXJzLCBhbmQgcG9pbnRzIGFyZSBleGFtcGxlcyBvZiAqZ2VvbWV0cmljIG9iamVjdHMgKGdlb21zKSogdGhhdCBjb3VsZCBiZSB1c2VkIHRvIHByZXNlbnQgdGhlIGRhdGEuCgoqIFRoZSBkYXRhIHVuaXRzIG5lZWQgdG8gYmUgY29udmVydGVkIHRvIHBoeXNpY2FsIHVuaXRzIGluIG9yZGVyIHRvIGJlIGRpc3BsYXllZC4gVGhpcyB1c2VzICpzY2FsaW5nKiBhbmQgYSAqY29vcmRpbmF0ZSBzeXN0ZW0qIChwb3NpdGlvbiBhZGp1c3RtZW50IGZvciB3aGVyZSBpcyBvdXIgcGl4ZWwgZ29pbmcpLiBPdGhlciAqc3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb25zKiBjYW4gYWxzbyBvY2N1ciwgc3VjaCBhcyBhZ2dyZWdhdGluZyBkYXRhLCBqaXR0ZXJpbmcsIGRlbnNpdHkgZXN0aW1hdGVzLCBhIGJveHBsb3QgYW5kIGJpbm5pbmcuIAoKKiAqRmFjZXR0aW5nKiBhbGxvd3MgdXMgdG8gcGxvdCBzdWJzZXRzIG9mIHRoZSBkYXRhLgoKVGhpcyBncmFtbWFyIGFsbG93cyB2ZXJ5IHNwZWNpZmljIGNvbnRyb2wgb3ZlciB5b3VyIHBsb3QgYW5kIHRoZSBhYmlsaXR5IHRvIGNoYW5nZSBmZWF0dXJlcyAoZWFzaWx5KS4gUGxvdHMgY2FuIGJlIHNjYWxlZCBhbmQgbWFkZQpwcmV0dHkgZW5vdWdoIGZvciBwdWJsaWNhdGlvbiBxdWFsaXR5IGltYWdlcy4KCmdncGxvdDIgd2FzIG1hZGUgdG8gaW50ZXJhY3Qgd2VsbCB3aXRoIHRpZHkgKGxvbmcpIGRhdGFzZXRzLiBJIGhhdmUgZm91bmQgdGhhdCBpZiBzb21lb25lIGlzIHNwZW5kaW5nIGxvdHMgb2YgdGltZSBmaWd1cmluZyBvdXQgaG93IHRvIG1ha2UgYSBzY2F0dGVycGxvdCwgaGlzIG9yIGhlciBkYXRhIGlzIHByb2JhYmx5IG5vdCBpbiB0aGUgY29ycmVjdCBmb3JtYXQgZm9yIGdncGxvdDIuCgoKIyNUaGUgR3JhbW1hciBvZiBHcmFwaGljcyBpbiBBY3Rpb246IE1ha2luZyBGaWd1cmVzIHdpdGggZ2dwbG90MgoKIyMjT3VyIERhdGFzZXQKCk1ldGFnZW5vbWljIDE2U3JSTkEgc2VxdWVuY2luZyBvZiBsYXRyaW5lcyBmcm9tIFRhbnphbmlhIGFuZCBWaWV0bmFtIGF0IGRpZmZlcmVudCBkZXB0aHMgKG11bHRpcGxlcyBvZiAyMGNtKS4gCgpXZSBoYXZlIDIgY3N2IGZpbGVzIChjaGFuZ2Ugb25lIHRvIHRzdiBvciB4bHN4IC0gbWF5YmUgYm90aCBhbmQgbWFrZSBhbiBhZGRpdGlvbmFsIGZpbGVzIGFuZCBnZXQgYSBnb29nbGUgc3ByZWFkc2hlZXQpOiAKMS4gQSBtZXRhZGF0YSBmaWxlIChOYW1pbmcgY29udmVudGlvbnM6IFtDb3VudHJ5X0xhdHJpbmVOb19EZXB0aF0pIHdpdGggc2FtcGxlIG5hbWVzIGFuZCBlbnZpcm9ubWVudGFsIHZhcmlhYmxlcy4KMi4gQSB0YWJsZSBvZiBzcGVjaWVzIGFidW5kYW5jZS4KCkIgVG9yb25kZWwsIEpISiBFbnNpbmssIE8gR3VudmlydXNkdSwgVVogSWpheiwgSiBQYXJraGlsbCwgRiBBYmRlbGFoaSwgVi1BIE5ndXllbiwgUyBTdWRnZW4sIFcgR2lic29uLCBBVyBXYWxrZXIsIGFuZCBDIFF1aW5jZS4KQXNzZXNzbWVudCBvZiB0aGUgaW5mbHVlbmNlIG9mIGludHJpbnNpYyBlbnZpcm9ubWVudGFsIGFuZCBnZW9ncmFwaGljYWwgZmFjdG9ycyBvbiB0aGUgYmFjdGVyaWFsIGVjb2xvZ3kgb2YgcGl0IGxhdHJpbmVzCk1pY3JvYmlhbCBCaW90ZWNobm9sb2d5LCA5KDIpOjIwOS0yMjMsIDIwMTYuIERPSToxMC4xMTExLzE3NTEtNzkxNS4xMjMzNAoKKioqCgojIyNTY2F0dGVycGxvdCBFeGFtcGxlCgpJIGhhdmUgc2F2ZWQgYSB2ZXJzaW9uIG9mIHRoZSBPVFUgdGFibGUgaW4gdGlkeSBmb3JtYXQgKHdoaWNoIHdlIGNyZWF0ZWQgaW4gTGVzc29uIDIgLSBzcGxpdF9kYXQpLiBBcyB3ZWxsIGFzIGEgbW9kaWZpZWQgdmVyc2lvbiBvZiB0aGUgbWV0YWRhdGEgdGFibGUuCgpgYGB7ciBpbmNsdWRlID0gRkFMU0V9CiNuZGF0IDwtIHJlYWQuY3N2KCJkYXRhL0VOVl9waXRsYXRyaW5lLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQojbmRhdCA8LSBuZGF0ICU+JSBzZXBhcmF0ZShTYW1wbGVzLCBjKCJDb3VudHJ5IiwgIkxhdHJpbmVfTnVtYmVyIiwgIkRlcHRoIiksIHNlcCA9ICJfIikKI25kYXQkQ291bnRyeSA8LSBnc3ViKCJUIiwgIlRhbnphbmlhIiwgbmRhdCRDb3VudHJ5KQojbmRhdCRDb3VudHJ5IDwtIGdzdWIoIlYiLCAiVmlldG5hbSIsIG5kYXQkQ291bnRyeSkKCiN3cml0ZS5jc3YobmRhdCwgImRhdGEvc3BsaXRfRU5WX3BpdGxhdHJpbmUuY3N2Iiwgcm93Lm5hbWVzPUZBTFNFKQoKbmRhdCA8LSByZWFkLmNzdigiZGF0YS9zcGxpdF9FTlZfcGl0bGF0cmluZS5jc3YiKQpgYGAKCgpMZXQncyByZWFkIHRoZW0gaW4gYW5kIGFsc28gbG9hZCBnZ3Bsb3QyLgoKCmBgYHtyLCBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoKX0KbGlicmFyeSh0aWR5dmVyc2UpCgpkYXQgPC0gcmVhZC5jc3YoImRhdGEvbG9uZ19TUEVfcGl0bGF0cmluZS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRiwgaGVhZGVyID0gVFJVRSkKCmBgYAoKCkxldCdzIGJ1aWxkIGEgcGxvdCBieSBhZGRpbmcgY29tcG9uZW50cyBvbmUgYnkgb25lIHRvIHNlZSBob3cgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MgaXMgaW1wbGVtZW50ZWQgaW4gZ2dwbG90Mi4gVG8gc3RhcnQsIHdlIG9mIGNvdXJzZSBuZWVkIHRvIGlucHV0IG91ciBkYXRhLiBIb3dldmVyLCBpZiB0aGF0IGlzIGFsbCB3ZSBpbnB1dCwgd2hhdCB3ZSBnZXQgYmFjayBpcyBhIGJsYW5rIGdyYXBoaWMuIFdlIGhhdmUgbm90IHlldCBzYWlkIHdoYXQgd2Ugd2FudCB0byBwbG90IGFuZCBob3cgd2Ugd2FudCB0byBwbG90IGl0LiAKCmBgYHtyfQpnZ3Bsb3QobmRhdCkKYGBgCgpXZSBoYXZlLCBob3dldmVyLCBjcmVhdGVkIGEgZ2dwbG90IG9iamVjdC4gVGhpcyBpcyBhIGxpc3Qgb2YgOSBwYXJhbWV0ZXJzOiBkYXRhLCBsYXllcnMsIHNjYWxlcywgbWFwcGluZywgdGhlbWUsIGNvb3JkaW5hdGVzLCBmYWNldCwgcGxvdCBlbnZpcm9ubWVudCwgYW5kIGxhYmVscy4gTHVja2lseSB0aGVyZSBhcmUgc29tZSBkZWZhdWx0cywgc28gd2UgZG9uJ3QgaGF2ZSB0byBzcGVjaWZ5IGV2ZXJ5dGhpbmcsIGJ1dCB5b3UgY2FuIHN0YXJ0IHRvIHNlZSBob3cgZ2dwbG90IG9iamVjdHMgYXJlIGhpZ2hseSBjdXN0b21pemFibGUuIAoKYGBge3J9CnN0cihnZ3Bsb3QobmRhdCkpCmBgYAoKVGhlIG5leHQgc3RlcCBpcyB0byBjaG9vc2UgdGhlIGRhdGEgd2UgYXJlIHBsb3R0aW5nIChhZXN0aGV0aWNzKS4gQXQgdGhpcyBwb2ludCB0aGUgZGF0YSBjYW4gYmUgc2NhbGVkIGFuZCB0aGUgYXhlcyBhcHBlYXIuIFdlIGhhdmUgbm90IHlldCBzcGVjaWZpZWQgaG93IHdlIHdhbnQgdGhlIGRhdGEgcGxvdCwganVzdCB3aGljaCBkYXRhIHNob3VsZCBiZSBwbG90dGVkLiBJbiBwcmFjdGljZSwgcGVvcGxlIHVzdWFsbHkgb21pdCAnbWFwcGluZyA9ICcsIGJ1dCBpdCBpcyBhIGdvb2QgcmVtaW5kZXIgdGhhdCBtYXBwaW5nIGlzLCBpbiBmYWN0LCB3aGF0IHdlIGFyZSBkb2luZy4KCmBgYHtyfQpnZ3Bsb3QobmRhdCwgbWFwcGluZyA9IGFlcyh4ID0gVFMsIHkgPSBDT0R0KSkKYGBgCgpXZSBub3cgaGF2ZSBjaG9zZW4gdGhlIGdlb21ldHJpYyBvYmplY3QgKGdlb20pIHdpdGggd2hpY2ggdG8gcGxvdCBvdXIgZGF0YSwgaW4gdGhpcyBjYXNlIGEgcG9pbnQuIEEgZ2VvbSBjb3VsZCBiZSBhIGxpbmUsIGEgYmFyLCBhIGJveHBsb3QgLSB5b3UgY2FuIHR5cGUgZ2VvbV8gYW5kIHRoZW4gVGFiIHRvIHNlZSBhbGwgb2YgdGhlIGF2YWlsYWJsZSBvcHRpb25zLiBBdXRvY29tcGxldGUgY2FuIGFsc28gYmUgaGVscGZ1bCBmb3IgcmVtZW1iZXJpbmcgc3ludGF4LgoKYGBge3J9CmdncGxvdChuZGF0LCBhZXMoeD1UUywgeT1DT0R0KSkgKyBnZW9tX3BvaW50KCkgCmBgYAoKVGhlIGRhdGEgbG9va3MgbGlrZSB0aGVyZSBhcmUgdHdvIGdyb3VwaW5ncy4gTXkgZ3Vlc3Mgd291bGQgYmUgdGhhdCB0aGlzIGlzIGZvciB0aGUgMiBkaWZmZXJlbnQgY291bnRyaWVzLiBXZSBjYW4gZWFzaWx5IHRlc3QgdGhpcyBieSBjb2xvdXJpbmcgb3VyIHBvaW50cyBieSBDb3VudHJ5LiBBIGxlZ2VuZCB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgY3JlYXRlZCBmb3IgeW91LiBJIGhhdmUgYWxzbyBjaGFuZ2VkIHRoZSBzaGFwZSBhbmQgc2l6ZSBvZiB0aGUgZ2VvbS4gQSBxdWljayByZWZlcmVuY2Uga2V5IGZvciBzaGFwZXMgY2FuIGJlIGZvdW5kIGluIHRoZSAnQ29va2Jvb2sgZm9yIFInIChodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9TaGFwZXNfYW5kX2xpbmVfdHlwZXMvKS4KCmBgYHtyfQpnZ3Bsb3QobmRhdCwgYWVzKHg9VFMsIHk9Q09EdCwgY29sb3VyID0gQ291bnRyeSkpICsgZ2VvbV9wb2ludChzaGFwZSA9IDE4LCBzaXplID0gNSkgCmBgYAoKU29tZSBvZiBvdXIgZGF0YSBwb2ludHMgc2VlbSB0byBiZSBjcnVzaGVkIG5lYXIgdGhlIHgtYXhpcy4gV2UgY2FuIHNjYWxlIHRoZSB5LWF4aXMgdG8gZml4IHRoaXMuIFdoZW4gd2Ugc3RhcnQgY3VzdG9taXppbmcgb3VyIHBsb3QsIGl0IGVhc2llciB0byByZWFkIG91ciBjb2RlIGlmIHdlIGhhdmUgZWFjaCBzcGVjaWZpY2F0aW9uIG9uIGEgbmV3IGxpbmUuIAoKCmBgYHtyfQpnZ3Bsb3QobmRhdCwgYWVzKHg9VFMsIHk9Q09EdCwgY29sb3VyID0gQ291bnRyeSkpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKyAKICBzY2FsZV95X2xvZzEwKCkKYGBgCgojIyMjRmFjZXRpbmcKCkZhY2V0aW5nIGFsbG93cyB1cyB0byBzcGxpdCBvdXIgZGF0YSBpbnRvIGdyb3Vwcy4gTm90ZSB0aGF0IEkgaGF2ZSByZW1vdmVkIHRoZSBjb2xvdXIuIEl0IGlzIGdvb2QgZGF0YSB2aXN1YWxpemF0aW9uIHByYWN0aWNlIHRvIG9ubHkgaGF2ZSBvbmUgYXR0cmlidXRlIChjb2xvdXIsIHNoYWRpbmcsIGZhY2V0aW5nLCBzeW1ib2xzKSBwZXIgZ3JvdXBpbmcuCgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PVRTLCB5PUNPRHQpKSArIAogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBmYWNldF9ncmlkKH5Db3VudHJ5KQpgYGAKCkkgY291bGQgbm93IGFkZCBpbmZvcm1hdGlvbiBmcm9tIGFub3RoZXIgdmFyaWFibGUgYXMgYSBjb2xvdXIgaW4gdGhpcyBwbG90LiBOb3RlIHRoYXQgaWYgYSB2YXJpYWJsZSBpcyBjb250aW51b3VzIGluc3RlYWQgb2YgZGlzY3JldGUsIHRoZSBjb2xvdXIgd2lsbCBiZSBhIGdyYWRpZW50LgoKYGBge3J9CmdncGxvdChuZGF0LCBhZXMoeD1UUywgeT1DT0R0LCBjb2xvdXIgPSBUZW1wKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZmFjZXRfZ3JpZCh+Q291bnRyeSkKYGBgCgpJZiB3ZSByZWFsbHkgd2FudGVkIHRvIHdlIGNvdWxkIGFkZCBhbm90aGVyIHZhcmlhYmxlIHRvIHRoZSBwbG90IGJ5IGNoYW5naW5nIHRoZSBzaGFwZSBhdHRyaWJ1dGUuIE5vdGUgdGhhdCBzaGFwZSBjYW4gb25seSBiZSB1c2VkIGZvciBkaXNjcmV0ZSB2YWx1ZXMuIExldCdzIGNoYW5nZSBDb3VudHJ5IHRvIGhhdmluZyAyIHNoYXBlcyBhbmQgZmFjZXQgYnkgdGhlIERlcHRoIG9mIHRoZSBsYXRyaW5lIGluc3RlYWQuCgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PVRTLCB5PUNPRHQsIGNvbG91ciA9IFRlbXAsIHNoYXBlID0gQ291bnRyeSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGZhY2V0X3dyYXAofkRlcHRoLCBzY2FsZXMgPSAiZnJlZV95IikKCmBgYAoKV2UgY2FuIG5vdyBub3RlIHRoYXQgb25seSBUYW56YW5pYSBoYWQgc2FtcGxpbmcgZ3JlYXRlciB0aGFuIDRjbSBvZiBkZXB0aC4gVGhlcmUgYXJlIHNpbmdsZSBsYXRyaW5lcyBmb3IgNCBzYW1wbGVzLiBUaGVyZSB3YXMgbm8gbGF0cmluZSBhdCBhIGRlcHRoIG9mIDExY20uIExhY2sgb2YgcmVwbGljYXRpb24gYW5kIGEgYmlhcyB0b3dhcmRzIFRhbnphbmlhIGZvciB0aGUgaGlnaGVyIGRlcHRocyBpcyBzb21ldGhpbmcgd2Ugc2hvdWxkIGtlZXAgaW4gbWluZCB3aGlsZSBsb29raW5nIGF0IHRoaXMgZGF0YS4gRGVwZW5kaW5nIG9uIHRoZSBxdWVzdGlvbiB3ZSBhcmUgdHJ5aW5nIHRvIGFuc3dlciwgd2UgbWF5IHdhbnQgdG8gcmVtb3ZlIHNvbWUgb2YgdGhpcyBkYXRhLiBXZSBjYW4gYWxzbyBub3RlIHRoYXQgb3VyIG51bWJlcnMgYXJlIGJlaW5nIG9yZGVyZWQgYXMgaWYgdGhleSB3ZXJlIGNoYXJhY3RlcnMuIElmIHlvdSByZWNhbGwgd2UgY3JlYXRlZCBEZXB0aCBieSBzcGxpdHRpbmcgYSBzYW1wbGUgbmFtZS4gV2UgY2FuIGNoYW5nZSBEZXB0aCB0byBudW1lcmljIHR5cGUsIGFuZCB0aGUgbnVtYmVycyB3aWxsIGJlIG9yZGVyZWQgcHJvcGVybHkuCgpgYGB7cn0KbmRhdCREZXB0aCA8LSBhcy5udW1lcmljKG5kYXQkRGVwdGgpCgpnZ3Bsb3QobmRhdCwgYWVzKHg9VFMsIHk9Q09EdCwgY29sb3VyID0gVGVtcCwgc2hhcGUgPSBDb3VudHJ5KSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZmFjZXRfd3JhcCh+RGVwdGgpCmBgYAoKT25lIHRoaW5nIHRoYXQgaXMgbm90IG5lY2Vzc2FyeSBpbiB0aGlzIGNhc2UgLSBidXQgZ29vZCB0byBrbm93IGFib3V0IC0gaXMgdGhlIGFiaWxpdHkgdG8gYWxsb3cgZWFjaCBncmlkIHRvIGhhdmUgaXRzIG93biBpbmRlcGVuZGVudCBheGlzIHNjYWxlLiBJbiBvdXIgZXhhbXBsZSwgd2VsbHMgb2YgRGVwdGhzIGZyb20gMS00Y20gaGF2ZSB1cCB0byAxMDAwIENPRHQsIHdoaWxlIHRoZSBvdGhlciB3ZWxscyBiYXJlbHkgaGF2ZSB2YWx1ZXMgcGFzdCAxMDAgQ09EdC4gVGhpcyBjYW4gYmUgY2hhbmdlZCwgYnV0IGtlZXAgaW4gbWluZCBtb3N0IHBlb3BsZSB3aWxsIGFzc3VtZSBhbGwgZ3JpZHMgaGF2ZSB0aGUgc2FtZSBzY2FsZSwgc28gdGFrZSBleHRyYSBjYXJlIHRvIHBvaW50IHRoYXQgdGhlIHNjYWxlcyBhcmUgZGlmZmVyZW50IHdoZW4gcHJlc2VudGluZyBvciBwdWJsaXNoaW5nLiAKCmBgYHtyfQpnZ3Bsb3QobmRhdCwgYWVzKHg9VFMsIHk9Q09EdCwgY29sb3VyID0gVGVtcCwgc2hhcGUgPSBDb3VudHJ5KSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZmFjZXRfd3JhcCh+RGVwdGgsIHNjYWxlcyA9ICJmcmVlX3kiKQoKYGBgCgojIyMjQWRkaW5nIFJlZ3Jlc3Npb24gTGluZXMKWW91IGNhbiBhbHNvIGFkZCBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMgdG8geW91ciBwbG90cy4gQWdhaW4sIHRha2UgYSBsb29rIGF0IHN0YXRfIHRoZW4gVGFiIHRvIHNlZSB0aGUgbGlzdCBvZiBvcHRpb25zLiBJbiB0aGlzIGNhc2UgbGV0J3Mgc2VwYXJhdGVseSBmaXQgYSBsaW5lYXIgcmVncmVzc2lvbiBsaW5lIHRvIENPRHQgdnMgVFMgZm9yIGVhY2ggY291bnRyeS4gVGhlIGdyZXkgYXJlYSBhcm91bmQgdGhlIGxpbmUgaXMgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgKGRlZmF1bHQ9MC45NSkgYW5kIGNhbiBiZSByZW1vdmVkIHdpdGggdGhlIGFkZGl0aW9uYWwgY2FsbCB0byBzdGF0X3Ntb290aCBvZiAnc2UgPSBGQUxTRScuCgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PVRTLCB5PUNPRHQpKSArIAogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBmYWNldF9ncmlkKH5Db3VudHJ5KSArCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gbG0pCmBgYAoKQSBsaW5lYXIgbW9kZWwgaXMgbm90IG5lY2Vzc2FyaWx5IHRoZSBiZXN0IGZpdC4gVGhlIG1ldGhvZCBvZiBjYWxjdWxhdGluZyB0aGUgc21vb3RoaW5nIGZ1bmN0aW9uIGNhbiBiZSBjaGFuZ2VkIHRvIG90aGVyIHByb3ZpZGVkIGZ1bmN0aW9ucyBvciBjYW4gYmUgYSBjdXN0b20gZm9ybXVsYS4gTm90ZSB0aGF0IEkgY2hhbmdlZCB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBieSBtb2RpZnlpbmcgJ2xldmVsPTAuOCcuIGdlb21zIGNhbiBiZSBtYWRlIG1vcmUgdHJhbnNwYXJlbnQgd2l0aCB0aGUgYWxwaGEgcGFyYW1ldGVyLCB3aGljaCBpcyBzZXQgdG8gMC4zIGluIHRoZSBmb2xsb3dpbmcgY29kZSBzbyB0aGF0IHRoZSBlbXBoYXNpcyBpcyBvbiB0aGUgcmVncmVzc2lvbiBsaW5lIHJhdGhlciB0aGFuIHRoZSBwb2ludHMuCgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PVRTLCB5PUNPRHQpKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGZhY2V0X2dyaWQofkNvdW50cnkpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSBsb2VzcywgbGV2ZWwgPSAwLjgpCmBgYAoKIyNFeHBsb3JpbmcgZGlmZmVyZW50IHR5cGVzIG9mIHBsb3RzCgojIyNEZW5zaXR5IFBsb3RzCgpXZSBhcmUgbWFraW5nIGEgZGVuc2l0eSBwbG90IGZvciBPVFVzIGJ5IENvdW50cnkuIEkgaGF2ZSBzZXQgYWxwaGEgKHRyYW5zcGFyZW5jeSkgdG8gMC4zIHNvIHRoYXQgd2UgY2FuIHNlZSBib3RoIGNvdW50cmllcyBvbiBvdXIgcGxvdC4KCmBgYHtyfQpnZ3Bsb3QoZGF0LCBhZXMoeD1PVFVzLCBmaWxsPUNvdW50cnksIGFscGhhPTAuMykpICsgCglnZW9tX2RlbnNpdHkoKSAKYGBgCgpUaGUgZmlyc3QgdGhpbmcgdG8gbm90aWNlIGlzIHRoYXQgZXZlcnl0aGluZyBpcyBjbHVtcGVkIGF0IDAuIFRoaXMgaXMgYmVjYXVzZSB3ZSBoYXZlIG5vdCBmaWx0ZXJlZCBvdXIgZGF0YSBmcmFtZSB0byByZW1vdmUgYWxsIG9ic2VydmF0aW9ucyB3aGVyZSBPVFVzIGFyZSB6ZXJvLiBIZXJlIHdlIGZpbHRlciB0byBoYXZlIGF0IGxlYXN0IDIgT1RVcy4gVGhlIG90aGVyIHRoaW5nIHRvIG5vdGljZSBpcyB0aGF0IHRoZXJlIGlzIGEgbG9uZyB0YWlsIHdoZXJlIHRoZXJlIHdpbGwgb25seSBiZSBhIGZldyBvYnNlcnZhdGlvbnMuIEl0IHdpbGwgYmUgbmVjZXNzYXJ5IHRvIGNoYW5nZSB0aGUgeC1heGlzIHRvIHNlZSBvdXIgZGF0YS4gTm90ZSB0aGF0IFIgZ2l2ZXMgdXMgYSB3YXJuaW5nIHRoYXQgd2UgYXJlIG5vdCB2aWV3aW5nIDE1OCBvZiBvdXIgNDIxMiByb3dzLiBXZSBjYW4gYWRkIGEgcnVnIGdlb20gdG8gc2VlIGVhY2ggdmFsdWUuCgpgYGB7ciB9CmdncGxvdChkYXRbZGF0JE9UVSA+PTIsXSwgYWVzKHg9T1RVcywgZmlsbD1Db3VudHJ5LCBhbHBoYT0wLjMpKSArIAoJZ2VvbV9kZW5zaXR5KCkgKwogIGdlb21fcnVnKCkgKwogIHhsaW0oMCwgMTAwMCkKCmBgYAoKIyMjSGlzdG9ncmFtcwoKSGlzdG9ncmFtcyBpbnN0ZWFkIGNvdW50IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHlvdSBoYXZlIGluIGVhY2ggJ2JpbicgdGhhdCB5b3Ugc3BlY2lmeS4gVGhlIGRlZmF1bHQgYmlud2lkdGggaXMgMzAuIFRISVMgSEFTIE5PVEhJTkcgVE8gRE8gV0lUSCBZT1VSIERBVEEhISBDSEFOR0UgSVQhISBSIHdpbGwgZXZlbiB3YXJuIHlvdSB0byBjaGFuZ2UgeW91ciBiaW53aWR0aC4KCmBgYHtyfQpnZ3Bsb3QoZGF0W2RhdCRPVFUgPj0yLF0sIGFlcyh4PU9UVXMsIGZpbGw9Q291bnRyeSwgYWxwaGE9MC4zKSkgKyAKCWdlb21faGlzdG9ncmFtKCkgKwogIHhsaW0oMCwxMDAwKQpgYGAKCkluc3RlYWQgb2YgaGF2aW5nIHRoZSBjb3VudHJpZXMgaW5mb3JtYXRpb24gc3RhY2tlZCwgd2UgbWF5IHdhbnQgdG8gc2VlIHRoZSBkYXRhIHNpZGUgYnkgc2lkZS4gVGhpcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBwYXJhbWV0ZXIgJ3Bvc2l0aW9uJyBzZXQgdG8gImRvZGdlIi4gQSBydWcgZ2VvbSBjYW4gYWxzbyBiZSBhZGRlZCB0byBhIGhpc3RvZ3JhbS4KCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KZ2dwbG90KGRhdFtkYXQkT1RVID49MixdLCBhZXMoeD1PVFVzLCBmaWxsPUNvdW50cnksIGFscGhhPTAuMykpICsgCglnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUwLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICB4bGltKDAsMTAwMCkgKwogIHlsaW0oMCwxNTApICsKICBnZW9tX3J1ZygpCmBgYAoKKioqCl9fQ2hhbGxlbmdlX18gCgoKPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDttYXJnaW46MCAxMHB4IDEwcHggMCIgbWFya2Rvd249IjEiPgohW10oaW1nL1B1Zy12cy1Gb29kLTEuanBnKXt3aWR0aD0xNTBweH0KCjwvZGl2PgoKVXNpbmcgdGhlIGRhdGEgZmlsdGVyZWQgZm9yIE9UVXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIHR3bywgbWFrZSBhIHZpb2xpbiBwbG90IG9mIHRoZSBkaXN0cmlidXRpb24gb2YgT1RVcyBmb3IgZWFjaCBjb3VudHJ5LiBVc2UgYSBsb2cgc2NhbGUuIENvbG91ciB0aGUgdmlvbGluIHBsb3RzIGJ5IENvdW50cnkuIERyYXcgbGluZXMgYWNyb3NzIHRoZSB2aW9saW4gcGxvdCB3aGVyZSB0aGUgcXVhbnRpbGVzICgyNXRoLCA1MHRoLCA3NXRoKSBhcmUgZm9yIGVhY2ggcGxvdC4gV2hhdCBkbyB0aGUgd2lkdGhzIG9mIHRoZSBwbG90cyByZXByZXNlbnQ/CgoKPC9icj4KPC9icj4KPC9icj4KCioqKgoKCgpgYGB7ciBpbmNsdWRlID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGRhdFtkYXQkT1RVID49MixdLCBhZXMoeD1Db3VudHJ5LCB5ID0gT1RVcywgZmlsbCA9IENvdW50cnkpKSArIAogIHNjYWxlX3lfbG9nMTAoKSArCglnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSkgCgpgYGAKCgoKCiMjI0JhciBwbG90cwoKTGV0J3MgY3JlYXRlIGEgYmFyIHBsb3Qgb2YgT1RVcyBwZXIgY291bnRyeSBhbmQgdXNlIHRoZSBmaWxsZWQgaW4gY29sb3VyIHRvIHJlcHJlc2VudCBUYXhhLiAKCmBgYHtyIGVycm9yID0gVFJVRX0KZ2dwbG90KGRhdCwgYWVzKHg9Q291bnRyeSwgeT1PVFVzLCBmaWxsPVRheGEpKSArIAoJZ2VvbV9iYXIoKSAKYGBgCgoKClRoaXMgaXMgYSBjb21tb24gZXJyb3IgYmVjYXVzZSB0aGUgZGVmYXVsdCBmb3IgZ2VvbV9iYXIgaXMgdG8gdXNlIHRoZSB5LWF4aXMgZm9yIGEgY291bnQuIFRvIHVzZSBpdCBmb3IgYSB2YXJpYWJsZSBpbnN0ZWFkLCB3ZSBoYXZlIHRvIHNwZWNpZnkgc3RhdD0iaWRlbnRpdHkiLgoKYGBge3J9CmdncGxvdChkYXRbZGF0JE9UVSA+PTIsXSwgYWVzKHg9Q291bnRyeSwgeT1PVFVzLCBmaWxsPVRheGEpKSArIAoJZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpIApgYGAKCiMjIyNDb250cm9sbGluZyB0aGUgb3JkZXIgb2YgeW91ciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW4geW91ciBsZWdlbmQKCkluIHRoZSBsZWdlbmQgZm9yIHRoZSBkaWFncmFtIGFib3ZlIG91ciBmYWN0b3IgbGV2ZWxzIGZvciBUYXhhIGFyZSBpbiBhbHBoYWJldGljYWwgb3JkZXIuIEhvd2V2ZXIsIGFuIG9yZGVyaW5nIHRoYXQgbWlnaHQgYmUgbW9yZSB1c2VmdWwgd291bGQgYmUgdGhlIG9yZGVyIHRoYXQgbWF0Y2hlcyBvdXIgZGF0YSwgZm9yIGV4YW1wbGUsIFRheGEgaW4gZGVzY2VuZGluZyBvcmRlciBvZiBPVFVzLiBUbyBkbyB0aGlzLCB3ZSBjYW4gdXNlIHRoZSBgZm9yY2F0c2AgcGFja2FnZS4gYGZjdF9yZW9yZGVyMigpYCB0YWtlcyB0aGUgZmFjdG9yIHdlIHdhbnQgdG8gb3JkZXIgYnkgKFRheGEpIGFuZCBvcmRlcnMgaXQgYnkgdGhlIHZhbHVlcyBnaXZlbiAoQ291bnRyeSwgT1RVcykuIFdlIHdpbGwgdGFsayBhYm91dCBjdXN0b21pemluZyBwbG90cyBsYXRlciBpbiB0aGlzIGxlc3NvbiwgYnV0IHRoZSBsYXN0IGxpbmUgb2YgY29kZSBpcyBvbmUgd2F5IHRvIHJlbW92ZSB0aGUgbGVnZW5kIHRpdGxlIChieSBtYWtpbmcgdGhlIGxlZ2VuZCB0aXRsZSAnYmxhbmsnKS4KCmBgYHtyfQpsaWJyYXJ5KGZvcmNhdHMpCgpnZ3Bsb3QoZGF0W2RhdCRPVFUgPj0yLF0sIGFlcyh4PUNvdW50cnksIHk9T1RVcywgZmlsbD1mY3RfcmVvcmRlcjIoVGF4YSwgQ291bnRyeSwgT1RVcykpKSArIAoJZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAKYGBgCgpOb3cgb3VyIGhpZ2hlc3QgYWJ1bmRhbmNlIFRheGEgaXMgdGhlIGZpcnN0IHZhbHVlIGluIG91ciBsZWdlbmQsIGFuZCB0aGlzIG1hdGNoZXMgdGhlIG9yZGVyIG9mIHRoZSBUYXhhIGluIG91ciBiYXIgZ3JhcGguIFRoZSBsZWdlbmQgb3JkZXIgbm93IGhhcyBtZWFuaW5nLiBUaGUgd2hpdGUgbGluZSBpbiB0aGUgVmlldG5hbSBiYXIgZ3JhcGggaXMgYSBUYXhhIGZvciB3aGljaCB0aGVyZSB3YXMgYW4gT1RVIHZhbHVlIGluIFRhbnphbmlhIGJ1dCBubyBkYXRhIGluIFZpZXRuYW0gKHNpbmNlIGFueXRoaW5nIDw9MiB3YXMgZmlsdGVyZWQgb3V0IG9mIHRoZSBkYXRhIHNldCkuIAoKCgoKTGV0J3MgdGFrZSBhIHNlY29uZCB0byBsb29rIGF0IHdoYXQgaGFwcGVucyB3aGVuIHN0YXQgZG9lcyBub3QgZXF1YWwgJ2lkZW50aXR5Jy4KCmBgYHtyfQpnZ3Bsb3QoZGF0LCBhZXMoeD1Db3VudHJ5LCBmaWxsPVRheGEpKSArIAoJZ2VvbV9iYXIoKSAKCmBgYAoKV2hhdCBpcyBiZWluZyBjb3VudGVkIGluc3RlYWQgb2YgT1RVcz8gV2h5IGRvZXMgdGhlIGNvbG91cmluZyBmb3IgVGF4YSBsb29rIHNvIGRpZmZlcmVudD8KCgpZb3UgY2FuIGFsdGVybmF0ZSBiZXR3ZWVuIHN0YWNrZWQgb3IgImRvZGdlZCIgZm9yIHdoZXRoZXIgeW91ciBiYXJzIGFyZSBvbiB0b3Agb2YgZWFjaCBvdGhlciBvciBuZXh0IHRvIGVhY2ggb3RoZXIgKHNwbGl0IGJ5IGEgZmFjdG9yIG9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlKSBvciBvbiB0b3AuCgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PURlcHRoLCB5PXBILCBmaWxsID0gQ291bnRyeSkpICsgCglnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgCmBgYAoKWW91IGNhbiBoYXZlIHlvdXIgYmFycyBob3Jpem9uYWxseSBpbnN0ZWFkIG9mIHZlcnN0aWNhbGx5IGJ5IHVzaW5nIGNvb3JkX2ZsaXAoKWwuCgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PURlcHRoLCB5PXBILCBmaWxsID0gQ291bnRyeSkpICsgCglnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGNvb3JkX2ZsaXAoKQoKYGBgCgojIyNDaXJjdWxhciBQbG90cwoKSW4gZ2dwbG90LCBjaXJjdWxhciBwbG90cyBhcmUgcmVsYXRlZCB0byBiYXIgZ3JhcGhzIC0gdGhleSBqdXN0IGhhdmUgZGlmZmVyZW50IGNvb3JkaW5hdGUgc3lzdGVtcy4gVGhlIGRlZmF1bHQgY29vcmRpbmF0ZSBzeXN0ZW0gaXMgY2FydGVzaWFuIGNvb3JkaW5hdGVzLCBhbmQgd2UgbmVlZCB0byBzd2l0Y2ggdG8gcG9sYXIgY29vcmRpbmF0ZXMgdG8gbWFrZSBhIGNpcmNsZS4gVGhpcyBpcyBhIENveGNvbWIgcGxvdCAtIHBIIGxldmVscyBpbmNyZWFzZSBhcyB5b3UgbW92ZSB1cCB0aGUgb3V0ZXIgcmluZ3MsIGFuZCBkZXB0aCBpbmNyZWFzZXMgYXMgeW91IG1vdmUgY2xvY2t3aXNlIGFyb3VuZCB0aGUgY2lyY2xlLiBDb2xvdXIgaXMgc3RpbGwgcmVwcmVzZW50ZWQgYnkgY291bnRyeS4gCgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PURlcHRoLCB5PXBILCBmaWxsID0gQ291bnRyeSkpICsgCglnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGNvb3JkX3BvbGFyKCkKYGBgCgpUbyBtYWtlIHlvdXIgY2xhc3NpYyB2ZW5uIGRpYWdyYW0sIHVzZSB0aGV0YSB0byBzcGVjaWZ5IHdoYXQgdmFyaWFibGUgaXMgZ29pbmcgdG8gYmUgdXNlZCB0byBtYWtlIHVwIHRoZSBhbmdsZXMgKHdpZHRoIG9mIHBpZSBzbGljZXMpLiBUbyB3cmFwIHRvIGEgZnVsbCBjaXJjbGUgaW5zdGVhZCBvZiBoYXZpbmcgc2VjdGlvbnMsIHRoZSB3aWR0aCBpcyBzZXQgdG8gb25lLgoKYGBge3J9CmdncGxvdChkYXQsIGFlcyh4PSIiLCB5PUNvdW50cnksIGZpbGwgPSBDb3VudHJ5KSkgKyAKCWdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikKCmBgYAoKCgojIyNMaW5lcyAKClRvIGRyYXcgYSBsaW5lIGdyYXBoLCB3ZSBzZWxlY3QgZ2VvbV9saW5lKCkuIAoKYGBge3J9CmdncGxvdChuZGF0LCBhZXMoeD1UZW1wLCB5PXBIKSkgKyBnZW9tX2xpbmUoKSAKYGBgCgpXZSBjYW4gY29sb3VyIHRoZSBsaW5lcyBmb3IgdGhlIGRpZmZlcmVudCBjb3VudHJpZXMuIExvb2tpbmcgYXQgb3VyIHByZXZpb3VzIGdyYXBoLCB3aGF0IG11c3QgaGF2ZSBoYXBwZW5lZCB3aGVuIHRoZXNlIHZhbHVlcyB3ZXJlIG92ZXIgdGhlIHNhbWUgcmFuZ2U/CgpgYGB7cn0KZ2dwbG90KG5kYXQsIGFlcyh4PVRlbXAsIHk9cEgsIGNvbG91ciA9IENvdW50cnkpKSArIGdlb21fbGluZSgpIApgYGAKCiMjIyNFcnJvciBiYXJzCgpUbyBwbG90IGVycm9yIGJhcnMsIHdlIG5lZWQgdG8gZ2l2ZSB0aGUgZ2VvbSwgZ2VvbV9lcnJvcmJhcigpLCB0aGUgaW50ZXJ2YWwgb3ZlciB3aGljaCB3ZSB3YW50IHRvIGRyYXcgdGhlIGJhci4gVGhpcyBpcyB1c3VhbGx5IHNvbWV0aGluZyBsaWtlIG9uZSBzdGFuZGFyZCBkZXZpYXRpb24gb3Igb25lIHN0YW5kYXJkIGVycm9yIG9yIGEgY29uZmlkZW5jZSBpbnRlcnZhbC4gTHVja2lseSwgd2UgaGF2ZSBsZWFybmVkIGhvdyB0byBjYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiB3aXRoIGRwbHlyLiBUaGVuLCBhbGwgd2UgbmVlZCB0byBkbyBpcyBhZGQgb3Igc3VidHJhY3QgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBmcm9tIG91ciBkYXRhIHBvaW50IHRvIGdldCB0aGUgYm91bmRzIG9mIHRoZSBlcnJvciBiYXIuIGdlb21fZXJyb3JiYXIoKSB0YWtlcyB0aGVzZSBib3VuZHMgcGFzc2VkIHRvIHRoZSBwYXJhbWV0ZXJzIHltYXggYW5kIHltaW4uIEluIHRoaXMgY2FzZSwgc2luY2UgdGhlcmUgd2VyZSBubyBzYW1wbGUgcmVwbGljYXRlcywgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBpcyB0YWtlbiBmcm9tIHRoZSBtZWFuIG9mIGFsbCBvZiB0aGUgc29pbCByZWFkaW5ncyBpbiBhIGdpdmVuIGNvdW50cnkgYXQgYSBnaXZlbiBkZXB0aC4KCmBgYHtyfQplcnJkYXQgPC0gbmRhdCAlPiUgZ3JvdXBfYnkoQ291bnRyeSwgRGVwdGgpICU+JSBtdXRhdGUobWVhbl9wSCA9IG1lYW4ocEgpKSAlPiUgbXV0YXRlKHNkX3BIPSBzZChwSCkpCgpnZ3Bsb3QoZXJyZGF0LCBhZXMoeD1UZW1wLCB5PXBILCBjb2xvdXIgPSBDb3VudHJ5KSkgKyAKICBnZW9tX2xpbmUoKSArIAogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49cEgtc2RfcEgsIHltYXggPSBwSCtzZF9wSCksIHdpZHRoID0gLjIsIGFscGhhID0gMC40KQpgYGAKCgoKCiMjI1N0cmlwcGxvdHMKClRoZSBmaXJzdCBwbG90cyB3ZSBtYWRlIHdlIHNjYXR0ZXJwbG90cyB3aXRoIDIgY29udGludW91cyB2YXJpYWJsZXMuIFdpdGggb25lIGRpc2NyZXRlIHZhcmlhYmxlIGFuZCBvbmUgY2F0ZWdvcmljYWwgdmFyaWFibGUsIHdlIGNhbiBtYWtlIGEgc3RyaXBwbG90LgoKCmBgYHtyfQpnZ3Bsb3QoZGF0LCBhZXMoeCA9IERlcHRoLCB5ID0gT1RVcykpICsgZ2VvbV9wb2ludCgpIApgYGAKCkFnYWluLCB3ZSBoYXZlIGEgbG90IG9mIHZhbHVlcyBjcnVzaGVkIG5lYXIgdGhlIHgtYXhpcy4gSXQgd2UgYWRkIGxvZyBzY2FsaW5nIHRvIHRoZSB5LWF4aXMsIHdlIGdldCBhbiBlcnJvciB0aGF0IHRoaXMgdHJhbnNmb3JtYXRpb24gY3JlYXRlZCBpbmZpbml0ZSB2YWx1ZXMuIFRoaXMgaXMgYmVjYXVzZSB3ZSBoYXZlIHplcm9zIGluIG91ciBkYXRhIHNldC4gS25vd2luZyB0aGlzLCB3ZSBjb3VsZCBpZ25vcmUgdGhlIHdhcm5pbmcsIG9yIGFkZCArMSB0byBlYWNoIE9UVS4KCmBgYHtyfQpnZ3Bsb3QoZGF0LCBhZXMoeCA9IERlcHRoLCB5ID0gT1RVcykpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeV9sb2cxMCgpIApgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0LCBhZXMoeCA9IERlcHRoLCB5ID0gKE9UVXMrMSkpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfbG9nMTAoKSAKYGBgCgpJbiBvcmRlciB0byBzZWUgdGhlIHBvaW50cyBhIGxpdHRsZSBiZXR0ZXIsIHdlIGNhbiAnaml0dGVyJyB0aGVtLgoKYGBge3J9CmdncGxvdChkYXQsIGFlcyh4ID0gRGVwdGgsIHkgPSAoT1RVcysxKSkpICsgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKSArIHNjYWxlX3lfbG9nMTAoKSArIGZhY2V0X3dyYXAofkNvdW50cnkpCmBgYAoKCiMjI0J1YmJsZSBQbG90cyArIExhYmVscwoKTGV0J3MgZ3JvdXAgb3VyIE9UVXMgYnkgQ291bnRyeSBhbmQgVGF4YSBhbmQgbG9vayBhdCB0aGUgVGF4YSBmb3IgdGhlIHRvcCAzMCBPVFVzLgoKV2l0aCBidWJibGUgcGxvdHMsIHdlIG5lZWQgdG8gcHJvdmlkZSB0aGUgc2l6ZSBvZiBvdXIgYnViYmxlcyBpbiBhIHdheSB0aGF0IGlzIHByb3BvcnRpb25hbCB0byBvdXIgZGF0YSwgYW5kIHByb3ZpZGUgYSBzY2FsZSB0byBtYXAgdGhlc2UgdG8uIAoKV2UgYXJlIGdvaW5nIHRvIG1ha2Ugb3VyIHBvaW50cyB3aXRoIGdlb21faml0dGVyIHNvIHRoYXQgb3VyIGJ1YmJsZXMgZG9uJ3Qgb3ZlcmxhcC4gUmVtZW1iZXIgdGhhdCB3ZSB3YW50IHRvIHNwZWNpZnkgd2hhdCB3ZSBhcmUgcGxvdHRpbmcgd2l0aCBhZXN0aGV0aWNzLiBXZSB3YW50IHRoZSBidWJibGVzIHJlcHJlc2VudGluZyBPVFVzX3Blcl9UYXhhLCBzbyB3ZSBkaXZpZGUgZWFjaCB2YWx1ZSBieSBwaSB3aGVuIGNhbGxpbmcgc2l6ZSBzaW5jZSBidWJibGVzIGFyZSBjaXJjbGVzLgoKYGBge3J9IApiZGF0IDwtIGRhdCAlPiUgc2VsZWN0KC1MYXRyaW5lX051bWJlciwgLURlcHRoKSAlPiUgZ3JvdXBfYnkoVGF4YSwgQ291bnRyeSkgJT4lIG11dGF0ZShPVFVzX3Blcl9UYXhhID0gc3VtKE9UVXMpKSAlPiUgc2VsZWN0KC1PVFVzKQoKYmRhdCA8LSB1bmlxdWUoYmRhdCkgJT4lIGFycmFuZ2UoZGVzYyhPVFVzX3Blcl9UYXhhKSkgJT4lIC5bMTozMCxdCgpnZ3Bsb3QoYmRhdCwgYWVzKHggPSBDb3VudHJ5LCB5ID0gT1RVc19wZXJfVGF4YSwgZmlsbD1UYXhhKSkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHNpemUgPSBPVFVzX3Blcl9UYXhhL3BpKSwgcGNoID0gMjEsIHNob3cubGVnZW5kID0gVFJVRSkgICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKQpgYGAKCkhvd2V2ZXIsIGl0IGlzIGhhcmQgdG8gdGVsbCBwcm9wb3J0aW9uYWxseSBob3cgZGlmZmVyZW50IHRoZXNlIHNpemVzIGFyZS4gTWFwcGluZyB2YWx1ZXMgdG8gYSBzY2FsZSBnaXZlcyB1cyBtb3JlIG9mIGFuIGlkZWEgb2YgdGhlaXIgcmVsYXRpdmUgc2l6ZXMuCgpgYGB7cn0KZ2dwbG90KGJkYXQsIGFlcyh4ID0gQ291bnRyeSwgeSA9IE9UVXNfcGVyX1RheGEsIGZpbGw9VGF4YSkpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGdlb21faml0dGVyKGFlcyhzaXplID0gT1RVc19wZXJfVGF4YS9waSksIHBjaCA9IDIxLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZT1jKDEsMzApKSArCiAgZ3VpZGVzKGZpbGw9RkFMU0UpCmBgYAoKYGdncGxvdDJgIHByb3ZpZGVzIDIgbWV0aG9kcyBvZiBsYWJlbGxpbmcuIExldCdzIGxvb2sgYXQgdGhlIGhlbHAgbWVudSB0byBmaW5kIG91dCB3aGF0IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIDIgb2YgdGhlbSBpcy4gV2UgaGF2ZSB0byBzcGVjaWZ5IGluIHdpdGggYWVzdGhldGljcywgdGhhdCB3ZSBhcmUgcGxvdHRpbmcgb3VyIGxhYmVsLiAKCmBgYHtyfQpnZ3Bsb3QoYmRhdCwgYWVzKHggPSBDb3VudHJ5LCB5ID0gT1RVc19wZXJfVGF4YSwgZmlsbD1UYXhhKSkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHNpemUgPSBPVFVzX3Blcl9UYXhhL3BpKSwgcGNoID0gMjEsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlPWMoMSwzMCkpICsKICBnZW9tX2xhYmVsKGFlcyhsYWJlbCA9IFRheGEpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKQpgYGAKClRoaXMgaXNuJ3QgZmFudGFzdGljIGJlY2F1c2Ugb3VyIGxhYmVscyBhcmUgb3ZlcmxhcHBpbmcuIFdoYXQgcGFyYW1ldGVycyBtaWdodCB5b3UgYmUgYWJsZSB0byB1c2UgdG8gbW92ZSB0aGUgbGFiZWxzPyBUcnkgdXNpbmcgdGhlbSB0byBnZXQgdGhlIGxhYmVscyB0byBub3Qgb3ZlcmxhcC4KCmdlb21fdGV4dCBoYXMgYW4gb3B0aW9uIHRvIGNoZWNrIGZvciBvdmVybGFwIG9mIGxhYmVscy4gZ2VvbV90ZXh0IGRvZXMgbm90IHByb3ZpZGUgYSBiYWNrZ3JvdW5kIGZvciB0aGUgbGFiZWwsIGFuZCBpdCBtYXkgYmUgaGFyZGVyIHRvIHRlbGwgd2hpY2ggbGFiZWwgYmVsb25ncyB0byBlYWNoIGRhdGEgcG9pbnQuCgpgYGB7cn0KZ2dwbG90KGJkYXQsIGFlcyh4ID0gQ291bnRyeSwgeSA9IE9UVXNfcGVyX1RheGEsIGZpbGw9VGF4YSkpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGdlb21faml0dGVyKGFlcyhzaXplID0gc3FydChPVFVzX3Blcl9UYXhhL3BpKSksIHBjaCA9IDIxLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZT1jKDEsMzApKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IFRheGEpLCBjaGVja19vdmVybGFwID0gVFJVRSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkKYGBgCgpXaGF0IGhhcHBlbmVkIGhlcmU/IFdoeSBkb24ndCB3ZSBoYXZlIGFzIG1hbnkgbGFiZWxzPyBMb29rIGluIHRoZSBoZWxwIG1lbnUgdG8gZXhwbGFpbiB0aGlzIGJlaGF2aW91ci4KCkkgZG9uJ3QgbGlrZSBmdXR6aW5nIHdpdGggbGFiZWwgcG9zaXRpb25zLCBzbyBJIHdlbnQgbG9va2luZyBmb3IgYSBwYWNrYWdlIHRoYXQgd291bGQgZG8gdGhpcyBmb3IgbWUuIGBnZ3JlcGVsKClgIHdpbGwgJ3JlcGVsJyB5b3VyIGxhYmVscyBhd2F5IGZyb20gZWFjaCBvdGhlciB3aXRob3V0IGdldHRpbmcgcmlkIG9mIHRoZW0uIExldCdzIGNoZWNrIGl0IG91dCB3aXRoIG91ciBidWJibGUgcGxvdCBsYWJlbHMuIEluc3RhbGwgYW5kIGxvYWQgZ2dyZXBlbC4gVGhlIGVxdWl2YWxlbnQgZnVuY3Rpb24gd2UgY2FuIHVzZSBpcyBgZ2VvbV9sYWJlbF9yZXBlbCgpYC4gVGhlIGZvcmNlIHBhcmFtZXRlciBhbGxvdyB5b3UgdG8gbW9kaWZ5IGhvdyBmYXIgeW91IHdhbnQgeW91ciBsYWJlbHMgcHVzaGVkIGF3YXkgZnJvbSBlYWNoIG90aGVyLiBIZXJlLCBjb2xvdXIgaXMgYmVpbmcgdXNlZCB0byBtYXAgdG8gb3VyIGRhdGEgcG9pbnRzIGJ1dCBnZ3JlcGVsIGNhbiBkaXRjaCB0aGUgYm94IGFuZCBjb25uZWN0IGEgbGluZSB0byBkYXRhIHBvaW50cyBpbnN0ZWFkLiBWYXJpYXRpb25zIG9uIHVzZSBjYW4gYmUgZm91bmQgaGVyZTogaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dncmVwZWwvdmlnbmV0dGVzL2dncmVwZWwuaHRtbC4KCmBgYHtyfQpsaWJyYXJ5KGdncmVwZWwpCgpnZ3Bsb3QoYmRhdCwgYWVzKHggPSBDb3VudHJ5LCB5ID0gT1RVc19wZXJfVGF4YSwgZmlsbD1UYXhhKSkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHNpemUgPSBzcXJ0KE9UVXNfcGVyX1RheGEvcGkpKSwgcGNoID0gMjEsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlPWMoMSwzMCkpICsKICBnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbCA9IFRheGEpLCBmb3JjZSA9IDIsc2hvdy5sZWdlbmQgPSBGQUxTRSwgc2VnbWVudC5zaXplPTApCmBgYAoKIyMjQm94cGxvdHMKCkJveHBsb3RzIGFyZSBhIGdyZWF0IHdheSB0byB2aXN1YWxpemUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB5b3VyIGRhdGEuIEFzIGEgcmVtaW5kZXIsIHRoZSB0aGljayBsaW5lIGluIHRoZSBjZW50ZXIgb2YgdGhlIGJveCBpcyB0aGUgbWVkaWFuLiBUaGUgdXBwZXIgYW5kIGxvd2VyIGVuZHMgb2YgeW91ciBib3ggYXJlIHRoZSBmaXJzdCBhbmQgdGhpcmQgcXVhcnRpbGVzIG9yIDI1dGggYW5kIDc1dGggcGVyY2VudGlsZXMgb2YgeW91ciBkYXRhLiBUaGUgd2hpc2tlcnMgZXh0ZW5kIHRvIHRoZSBsYXJnZXN0IHZhbHVlIG5vIGZ1cnRoZXIgdGhhbiAxLjUqSVFSIChpbnRlci1xdWFydGlsZSByYW5nZSAtIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBmaXJzdCBhbmQgdGhpcmQgcXVhcnRpbGVzKS4gRGF0YSBiZXlvbmQgdGhlc2Ugd2hpc2tlcnMgYXJlIGNvbnNpZGVyZWQgb3V0bGllcnMgYW5kIHBsb3R0ZWQgYXMgaW5kaXZpZHVhbCBwb2ludHMuIFRoaXMgaXMgYSBxdWljayB3YXkgdG8gc2VlIGhvdyBjb21wYXJhYmxlIHlvdXIgc2FtcGxlcyBvciB2YXJpYWJsZXMgYXJlLgoKV2UgYXJlIGdvaW5nIHRvIHNlZSBib3hwbG90cyBmb3IgdGhlIGRpc3RyaWJ1dGlvbiBvZiBPVFVzIHBlciBUYXhhIGFjcm9zcyBhbGwgc2FtcGxlcy4KCmBgYHtyfQpnZ3Bsb3QoZGF0LCBhZXMoeCA9IFRheGEsIHkgPSBPVFVzKSkgKyBnZW9tX2JveHBsb3QoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkKYGBgCgpXaGlsZSBpdCBpcyBjbGVhciB0aGF0IENsb3N0cmlkaWEgaXMgdGhlIG1vc3QgcmVwcmVzZW50ZWQgdGF4YSwgaXQgaXMgZGlmZmljdWx0IHRvIHRlbGwgd2hldGhlciBzb21lIG90aGVyIHRheGEgaGF2ZSBubyByZXByZXNlbnRhdGlvbiwgb3IgaWYgdGhleSBhcmUgbG93bHkgcmVwcmVzZW50ZWQuIFRyYW5zZm9ybWluZyB0byBhIGxvZyBzY2FsZSBvbiB0aGUgeS1heGlzIHdpbGwgc29ydCB0aGlzIG91dCBmb3IgdXMuCgpgYGB7cn0KZ2dwbG90KGRhdCwgYWVzKHggPSBUYXhhLCB5ID0gT1RVcykpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgc2NhbGVfeV9sb2cxMCgpCmBgYAoKV2UgY291bGQgYWxzbyBjbGVhbiB0aGlzIHBsb3QgdXAgYSBiaXQgYnkgcmVtb3ZpbmcgdGhvc2UgdGF4YSB3aXRoIDEgb3IgZmV3ZXIgT1RVcy4gQW5kIGhhdmUgdGhlIGNvbmRpdGlvbiB0aGF0IGl0IG11c3Qgb2NjdXIgaW4gbW9yZSB0aGFuIDEgc2FtcGxlLgoKYGBge3J9CgprZWVwIDwtIGRhdCAlPiUgdW5ncm91cCgpICU+JSBmaWx0ZXIoT1RVcz49MikgJT4lIGdyb3VwX2J5KFRheGEpICU+JSBzdW1tYXJpemUobiA9IG4oKSkgJT4lIGZpbHRlcihuID4gMSkgJT4lIHNlbGVjdChUYXhhKQoKCmdncGxvdChkYXRbZGF0JFRheGEgJWluJSBrZWVwJFRheGEsXSAsIGFlcyh4ID0gVGF4YSwgeSA9IE9UVXMpKSArIGdlb21fYm94cGxvdCgpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIHNjYWxlX3lfbG9nMTAoKQpgYGAKCklmIHdlIGZhY2V0IGJ5IGNvdW50cnkgd2UgY2FuIHNlZSB0aGF0IEFjaWRvYmFjdGVyaWFfR3AxLCBmb3IgZXhhbXBsZSwgd2FzIG9ubHkgZm91bmQgaW4gVmlldG5hbSBhbmQgbm90IFRhbnphbmlhLgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQpnZ3Bsb3QoZGF0W2RhdCRUYXhhICVpbiUga2VlcCRUYXhhLF0gLCBhZXMoeCA9IFRheGEsIHkgPSBPVFVzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyBmYWNldF9ncmlkKH5Db3VudHJ5KQpgYGAKCkZvciBub3csIGxldCdzIGp1c3Qga2VlcCB0aGUgVGF4YSB0aGF0IGFyZSBjb21tb24gdG8gYm90aCBjb3VudHJpZXMuCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmtlZXAgPC0gZGF0ICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGZpbHRlcihPVFVzPj0yKSAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeSwgVGF4YSkgJT4lIAogIHN1bW1hcml6ZShuID0gbigpKSAlPiUgCiAgZmlsdGVyKG4gPiAxKSAKCmtlZXAkZHVwIDwtIGtlZXAgJT4lIHVuZ3JvdXAoKSAlPiUgc2VsZWN0KFRheGEpICU+JSBkdXBsaWNhdGVkKC4pCgprZWVwIDwtIGtlZXBba2VlcCRkdXAsICJUYXhhIl0KCmdncGxvdChkYXRbZGF0JFRheGEgJWluJSBrZWVwJFRheGEsXSAsIGFlcyh4ID0gVGF4YSwgeSA9IE9UVXMpKSArIGdlb21fYm94cGxvdCgpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIHNjYWxlX3lfbG9nMTAoKSArIGZhY2V0X2dyaWQofkNvdW50cnkpCgpgYGAKCiMjI0JlZXN3YXJtIFBsb3RzCgpFdmVuIHRob3VnaCBib3hwbG90cyBnaXZlIHVzIHN1bW1hcnkgc3RhdGlzdGljcyBvbiBvdXIgZGF0YSwgaXQgaXMgdXNlZnVsIHRvIGJlIGFibGUgdG8gc2VlIHdoZXJlIG91ciBkYXRhIHBvaW50cyBhcmUuIEFkZGluZyB0aGUgZGF0YSB1c2luZyBgZ2VvbV9wb2ludCgpYCBhZGRzIG91ciBkYXRhIGluIHRoZSBmb3JtIG9mIGEgc3RyaXBwbG90IHRvIG91ciBib3hwbG90cy4gSXQgd291bGQgYmUgbmljZSB0byBzZWUgaG93IG1hbnkgcG9pbnRzIHdlIGhhdmUgYXQgZWFjaCBPVFUgdmFsdWUsIGJ1dCB0aGVyZSBhcmUgYSBsb3Qgb2YgcG9pbnRzIG92ZXJsYXBwaW5nIGhlcmUuCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmdncGxvdChkYXRbKGRhdCRUYXhhPT0iQ2xvc3RyaWRpYSIgfCBkYXQkVGF4YSA9PSAiR2FtbWFwcm90ZW9iYWN0ZXJpYSIgfCBkYXQkVGF4YSA9PSAiVW5rbm93biIgfCBkYXQkVGF4YSA9PSAiQmFjaWxsaSIpICYgZGF0JENvdW50cnkgPT0gIlQiLF0gLCBhZXMoeCA9IFRheGEsIHkgPSBPVFVzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGdlb21fcG9pbnQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIAogIHNjYWxlX3lfbG9nMTAoKQpgYGAKCkppdHRlcmluZyB0aGUgcG9pbnRzIGdpdmVzIGEgZ2VuZXJhbCBpZGVhLCBidXQgaXMgYSBiaXQgdG9vIHdpZGVseSBkaXNwdXJzZWQgdG8gZ2l2ZSBhIGdvb2Qgc2Vuc2Ugb2YgdGhlIGRhdGEuCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmdncGxvdChkYXRbKGRhdCRUYXhhPT0iQ2xvc3RyaWRpYSIgfCBkYXQkVGF4YSA9PSAiR2FtbWFwcm90ZW9iYWN0ZXJpYSIgfCBkYXQkVGF4YSA9PSAiVW5rbm93biIgfCBkYXQkVGF4YSA9PSAiQmFjaWxsaSIpICYgZGF0JENvdW50cnkgPT0gIlQiLF0gLCBhZXMoeCA9IFRheGEsIHkgPSBPVFVzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGdlb21faml0dGVyKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkgKyAKICBzY2FsZV95X2xvZzEwKCkKYGBgCgpBIGJlZXN3YXJtIHBsb3QgcGxhY2VzIGRhdGEgcG9pbnRzIHRoYXQgd2VyZSBvdmVybGFwcGluZyBpbnN0ZWFkIG5leHQgdG8gZWFjaCBvdGhlciwgc28gd2UgY2FuIGdldCBhIGJldHRlciBwaWN0dXJlIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgcG9pbnRzLiBXZSBzaW1wbHkgb3ZlcmxheSB0aGUgcG9pbnRzIHdpdGggYGdlb21fYmVlc3dhcm0oKWAuCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkoZ2diZWVzd2FybSkKCmdncGxvdChkYXRbKGRhdCRUYXhhPT0iQ2xvc3RyaWRpYSIgfCBkYXQkVGF4YSA9PSAiR2FtbWFwcm90ZW9iYWN0ZXJpYSIgfCBkYXQkVGF4YSA9PSAiVW5rbm93biIgfCBkYXQkVGF4YSA9PSAiQmFjaWxsaSIpICYgZGF0JENvdW50cnkgPT0gIlQiLF0gLCBhZXMoeCA9IFRheGEsIHkgPSBPVFVzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGdlb21fYmVlc3dhcm0oKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIAogIHNjYWxlX3lfbG9nMTAoKQpgYGAKCgpJbmNyZWFzaW5nIHRoZSBzcGFjaW5nIGJldHdlZW4gZGF0YSBwb2ludHMgY2FuIG1ha2UgdGhpcyBkaXN0cmlidXRpb24gYSBiaXQgY2xlYXJlci4KCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KZ2dwbG90KGRhdFsoZGF0JFRheGE9PSJDbG9zdHJpZGlhIiB8IGRhdCRUYXhhID09ICJHYW1tYXByb3Rlb2JhY3RlcmlhIiB8IGRhdCRUYXhhID09ICJVbmtub3duIiB8IGRhdCRUYXhhID09ICJCYWNpbGxpIikgJiBkYXQkQ291bnRyeSA9PSAiVCIsXSAsIGFlcyh4ID0gVGF4YSwgeSA9IE9UVXMpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZ2VvbV9iZWVzd2FybShjZXggPSAyLjIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgCiAgc2NhbGVfeV9sb2cxMCgpCmBgYAoKVXNpbmcgYGdlb21fcXVhc2lyYW5kb20oKWAgZ2l2ZXMgdGhlIGVtcGlyaWNhbCBkaXN0cmlidXRpb24gb2YgdGhlIHN0cmlwcGxvdCB0byBhdm9pZCBvdmVycGxvdHRpbmcuCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmdncGxvdChkYXRbKGRhdCRUYXhhPT0iQ2xvc3RyaWRpYSIgfCBkYXQkVGF4YSA9PSAiR2FtbWFwcm90ZW9iYWN0ZXJpYSIgfCBkYXQkVGF4YSA9PSAiVW5rbm93biIgfCBkYXQkVGF4YSA9PSAiQmFjaWxsaSIpICYgZGF0JENvdW50cnkgPT0gIlQiLF0gLCBhZXMoeCA9IFRheGEsIHkgPSBPVFVzKSkgKyAKICBnZW9tX2JveHBsb3QoYWxwaGE9MC44KSArIAogIGdlb21fcXVhc2lyYW5kb20odmFyd2lkdGggPSBUUlVFKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIAogIHNjYWxlX3lfbG9nMTAoKQpgYGAKCgpPdGhlciBzcGFjaW5nIGFuZCBkaXN0cmlidXRpb24gb3B0aW9ucyBhcmUgYXZhaWxhYmxlIGF0IGh0dHBzOi8vZ2l0aHViLmNvbS9lY2xhcmtlL2dnYmVlc3dhcm0uCgoKCiMjQ3VzdG9taXphdGlvbiBvZiBQbG90IEVsZW1lbnRzIFJlbGF0ZWQgdG8gWW91ciBEYXRhCgpQbG90IGVsZW1lbnRzIHJlbGF0aW5nIHRvIG15IGRhdGEgaW5jbHVkZSB0aGluZ3MgbGlrZSBheGlzIGxhYmVscywgdGl0bGVzLCBjb2xvdXIgb3Igc2hhcGVzIHRoYXQgcmVwcmVzZW50IHN1YnNldHMgb2YgeW91ciBkYXRhLCBzY2FsaW5nIHRoYXQgaXMgZGF0YS1kZXBlbmRlbnQsIGxlZ2VuZHMsIGFuZCBvdGhlciBkYXRhLWRyaXZlbiBwYXJhbWV0ZXJzLiAKCkZvciBjdXN0b21pemluZyB5b3UgZGF0YSBpdCBpcyBwb3NzaWJsZSB0byBjaGFuZ2U6Ci0gY29sb3VyKCkKLSBmaWxsKCkKLSBzaGFwZSgpCi0gc2l6ZSgpCi0gYWxwaGEoKQoKV2UgaGF2ZSBzZWVuIGluIHRoZSBhYm92ZSBleGFtcGxlcyB0aGF0IGNvbG91ciBjYW4gYmUgYXBwbGllZCB0byBkaXNjcmV0ZSBvciBjb250aW5vdXMgdmFyaWFibGVzLiBXZSBjYW4gYWxzbyB1c2UgY29sb3VyIChzaGFwZSwgZXRjLikgdG8gcmVwcmVzZW50IG91dGxpZXJzLiBJbiB0aGlzIGRhdGEgc2V0IG91dGxpZXJzIGJleW9uZCB0aGUgd2hpc2tlcnMgKGFib3ZlIG9yIGJlbG93IDEuNSpJUVIpIGNhbiBiZSBjb2xvdXJlZCByZWQuCgpUaXRsZXMgYW5kIGF4aXMgbGFiZWxzIGNhbiBiZSBhZGRlZCB1c2luZzoKLSBnZ3RpdGxlKCkKLSB4bGFiKCkKLSB5bGFiKCkKCkluIHRoaXMgY2FzZSwgSSB3YW50IHRvIGFkZCBhIHRpdGxlLCBtb2RpZnkgdGhlIHktYXhpcyBsYWJlbCB0byBzYXkgdGhhdCB0aGUgZGF0YSBpcyBsb2csIGFuZCByZW1vdmUgdGhlIHgtYXhpcyBsYWJlbCBiZWNhdXNlIEkgdGhpbmsgaXQgaXMgc2VsZi1leHBsYW5hdG9yeSBhbmQgSSB3aWxsIG1lbnRpb24gVGF4YSBpbiBteSB0aXRsZS4gTm90ZSB0aGF0IEkgcmVtb3ZlIHRoZSB4LWF4aXMgbGFiZWwgYnkgdXNpbmcgJ05VTEwnLgoKSSBhbHNvIHdhbnQgdG8gY2hhbmdlIG15IENvdW50cnkgbGFiZWxzIGZyb20gYSBzaW5nbGUgbGV0dGVyIHRvIHRoZSBjb3VudHJ5IG5hbWUuIFRoaXMgY2FuIGJlIGRvbmUgaW4gYSBjb3VwbGUgb2Ygd2F5cy4gT25lIHdheSB3b3VsZCBiZSB0byBjaGFuZ2UgdGhlIHZhbHVlcyBpbiB0aGUgZGF0YXNldC4gU2luY2Ugd2UgaGF2ZW4ndCBsZWFybmVkIHN0cmluZyBtYW5pcHVsYXRpb24geWV0LCBJIHdpbGwgc2hvdyB5b3UgYSBzZWNvbmQgd2F5LCB3aGljaCBpcyB0byB1c2UgdGhlIGxhYmVsbGVyIGZ1bmN0aW9uLiBJIGNhbiBtYWtlIGEgdmVjdG9yIG9mIHRoZSBuYW1lcyB0aGF0IEkgd2FudCB0byByZXBsYWNlLiBUaGUgZGF0YSBpcyBzcGxpdCBieSBDb3VudHJ5IGluIHRoZSBgZmFjZXRfZ3JpZCgpYCBhbmQgdGhpcyBpcyB3aGVyZSB3ZSBwYXNzIG91ciBsYWJlbHMgdG8gdGhlIGxhYmVsbGVyIGZ1bmN0aW9uLCB3aGljaCB3aWxsIG91dHB1dCB0aGUgbmFtZXMgb24gdGhlIHN0cmlwIGxhYmVsLgoKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KbGFiZWxzIDwtIGMoVCA9ICJUYW56YW5pYSIsIFYgPSAiVmlldG5hbSIpCgpwIDwtIGdncGxvdChkYXRbZGF0JFRheGEgJWluJSBrZWVwJFRheGEsXSAsIGFlcyh4ID0gVGF4YSwgeSA9IE9UVXMsIGZpbGwgPSBUYXhhKSkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXIgPSAicmVkIikgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBmYWNldF9ncmlkKH5Db3VudHJ5LCBsYWJlbGxlciA9IGxhYmVsbGVyKENvdW50cnkgPSBsYWJlbHMpKSArCiAgZ2d0aXRsZSgiQWJ1bmRhbmNlIG9mIFRheGEgYnkgQ291bnRyeSIpICsKICB4bGFiKE5VTEwpICsKICB5bGFiKCJsb2coT1RVcykiKSArIAogIGd1aWRlcyhmaWxsPUZBTFNFKQoKcApgYGAKCgpBIGNvbW1vbiB0aGluZyB0byB3YW50IHRvIGRvIGlzIHRvIGNoYW5nZSBjb2xvdXJzIGZyb20gYGdncGxvdDJgJ3MgcmFpbmJvdyBjb2xvciBzY2hlbWUuIExldCdzIGNyZWF0ZSBvdXIgb3duIGNvbG91ciBwYWxldHRlIGZvciBUYXhhLgoKIyNBIE5vdGUgb24gQ29sb3VyIFBhbGV0dGVzCgpUaGVyZSBhcmUgMyBtYWluIHR5cGVzIG9mIGNvbG91ciBwYWxldHRlcy4KMS4gU2VxdWVudGlhbCAtIGltcGxpZXMgYW4gb3JkZXIgdG8geW91ciBkYXRhIGllLiBsaWdodCB0byBkYXJrIGltcGxpZXMgbG93IHZhbHVlcyB0byBoaWdoIHZhbHVlcy4KMi4gRGl2ZXJnaW5nIC0gbG93IGFuZCBoaWdoIHZhbHVlcyBhcmUgZXh0cmVtZXMsIGFuZCB0aGUgbWlkZGxlIHZhbHVlcyBhcmUgaW1wb3J0YW50IHN0aWxsIGdvZXMgZnJvbSBsaWdodCB0byBkYXJrLCBidXQgMyBjb2xvdXIgbWFpbmx5IHVzZWQuCjMuIFF1YWxpdGF0aXZlIC0gdGhlcmUgaXMgbm8gcXVhbnRpdGF0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNvbG91cnMuIFRoaXMgaXMgdXN1YWxseSB1c2VkIGZvciBjYXRlZ29yaWNhbCBkYXRhLgoKV2hpY2ggb2YgdGhlc2UgaXMgYGdncGxvdDJgJ3MgZGVmYXVsdCBjb2xvciBwYWxldHRlPwoKTWFueSBjb2xvdXIgcGFsZXR0ZXMgbm93IGV4aXN0LiBJIHNob3djYXNlIGEgY291cGxlIHRoYXQgd29yayBuaWNlbHkgd2l0aCBgZ2dwbG90MmAuIFRoZXNlIHBhY2thZ2VzIGFsc28gaGF2ZSBjb2xvcmJsaW5kIGZyaWVuZGx5IG9wdGlvbnMuIFJDb2xvckJyZXcgaGFzIG9wdGlvbnMgZm9yIHRoZXNlIDMgdHlwZXMgb2YgcGFsZXR0ZXMsIHdoaWNoIHlvdSBjYW4gc2VlIHdpdGggYGRpc3BsYXkuYnJld2VyLmFsbCgpYC4gV2l0aCBhIHNtYWxsZXIgZGF0YXNldCwgd2UgY291bGQgbWFrZSBhIGNhbGwgdG8gYHNjYWxlX2ZpbGxfYnJld2VyKClgLCB3aGljaCBqdXN0IHJlcXVpcmVzIGEgY2hvaWNlIG9mIG9uZSBvZiBSQ29sb3JCcmV3ZXIncyBwYWxldHRlcywgc3VjaCBhcyAiU3BlY3RyYWwiLiBIb3dldmVyLCB3ZSBoYXZlIDI0IFRheGEgYW5kIHRoZXNlIHBhbGV0dGVzIGhhdmUgOC0xMiBjb2xvdXJzLCBzbyB3ZSBoYXZlIHRvIGdldCBjcmVhdGl2ZS4gSSBoYXZlIHNpbXBseSB0YWtlbiB0aGUgMiBxdWFsaXRhdGl2ZSBwYWxldHRlcyB0aGF0IGVhY2ggaGF2ZSBhIGxlbmd0aCBvZiAxMiwgcHV0IHRoZW0gaW50byBvbmUgcGFsZXR0ZSwgYW5kIG1hZGUgc3VyZSBteSB2YWx1ZXMgd2VyZSB1bmlxdWUuIFRoaXMgY2FuIHRoZW4gYmUgcGFzc2VkIHRvIGdncGxvdCB2aWEgYHNjYWxlX2ZpbGxfbWFudWFsKClgLiBZb3UgY2FuIGFsd2F5cyBjaG9vc2UgYSB2ZWN0b3Igb2YgeW91ciBvd24gY29sb3JzIHVzaW5nIHRoaXMgJ1IgY29sb3IgY2hlYXRzaGVldCcoaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvfmZyYXppZXIvUlNwYXRpYWxHdWlkZXMvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYpLgoKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkodmlyaWRpcykKCmRpc3BsYXkuYnJld2VyLmFsbCgpCgojcCArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKQojeW91IGNhbiBhbHNvIHVzZSB0aGUgbmFtZXMgb2YgY29sb3Vycwojc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJjb3JuZmxvd2VyYmx1ZSIsICJncmV5IiwgInllbGxvdyIsICJvcmFuZ2UiLCAiYnJvd24iKSkKCnBhbGV0dGUxIDwtIGJyZXdlci5wYWwoMTIsICJQYWlyZWQiKQpwYWxldHRlMiA8LSBicmV3ZXIucGFsKDEyLCAiU2V0MyIpCgpsZW5ndGgodW5pcXVlKGMocGFsZXR0ZSwgcGFsZXR0ZTIpKSkKCmN1c3RvbSA8LSBjKHBhbGV0dGUxLCBwYWxldHRlMikKCnAgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b20pCgpwICsgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSkKcCArIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZSA9IFRSVUUsIG9wdGlvbiA9ICJwbGFzbWEiKQoKYGBgCgoKVGhlIGB2aXJpZGlzYCBwYWNrYWdlIGFsc28gaGFzIHNvbWUgbmljZSBjb2xvciBwYWxldHRlcyAoaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3ZpcmlkaXMvdmlnbmV0dGVzL2ludHJvLXRvLXZpcmlkaXMuaHRtbCkuIEkgdGhpbmsgdGhleSBtaWdodCBhbGwgYmUgZGl2ZXJnaW5nIHBhbGV0dGVzIChxdWFsaXRhdGl2ZSBpcyBiZXN0IGZvciBvdXIgVGF4YSksIGJ1dCBJIHdpbGwgc2hvd2Nhc2UgYSBjb3VwbGUgaGVyZS4KCmBgYHtyfQpsaWJyYXJ5KHZpcmlkaXMpCgpwICsgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSkKcCArIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZSA9IFRSVUUsIG9wdGlvbiA9ICJwbGFzbWEiKQpgYGAKCgpgUlNraXR0bGVCcmV3ZXJgIGlzIGFub3RoZXIgb3B0aW9uIGZvciBmdW5reSBjb2xvdXIgcGFsZXR0ZXMuCmBnZ3NjaWAgaGFzIGEgdmFyaWV0eSBvZiBjb2xvciBwYWxldHRlcyBpbnNwaXJlZCBieSBkaWZmZXJlbnQgc2NpZW50aWZpYyBqb3VybmFscyBhcyB3ZWxsIGFzIHRlbGV2aXNpb24gc2hvd3MgKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3NjaS92aWduZXR0ZXMvZ2dzY2kuaHRtbCkuCgoKIyNDdXN0b21pemF0aW9uIG9mIFRoZW1lCgpBcyBtZW50aW9uZWQgZWFybGllciwgaXQgaXMgcG9zc2libGUgdG8gY3VzdG9taXplIGV2ZXJ5IHNpbmdsZSBhc3BlY3Qgb2YgYSBnZ3Bsb3QuIE1vc3Qgb2YgdGhpcyBvY2N1cnMgd2l0aCBhIGNhbGwgdG8gX190aGVtZV9fLCB3aGljaCB5b3UgY2FuIHRoaW5rIG9mIGFzIG1vZGlmeWluZyBldmVyeXRoaW5nIEJVVCB5b3VyIGRhdGEuIEZvciBleGFtcGxlLCBteSBheGlzIGxhYmVscyBjYW4gYmUgbW9kaWZpZWQsIGJ1dCB0aGV5IChob3BlZnVsbHkpIGhhdmUgc29tZXRoaW5nIHRvIGRvIHdpdGggbXkgZGF0YS4gSG93ZXZlciwgY2hhbmdpbmcgdGhlIHNpemUgb2YgdGhlIHRleHQgb3IgdGhlIGZvbnQgb2YgdGhlIGxhYmVscyBpcyB1bnJlbGF0ZWQgdG8gbXkgZGF0YSwgYW5kIHRoYXQgc2FtZSBzdHJ1Y3R1cmUgY291bGQgYmUgY2FycmllZCBvdmVyIHRvIG90aGVyIHBsb3RzIGlmIEkgc2F2ZWQgbXkgb3duIHRoZW1lLgoKVGhpbmdzIHRoYXQgeW91IGNhbiBjaGFuZ2Ugd2l0aCB0aGVtZSBpbmNsdWRlIHRoZSBheGlzLCBsZWdlbmQsIHBhbmVscywgZ3JpZGxpbmVzLCBvciBiYWNrZ3JvdW5kLgoKRWFjaCBlbGVtZW50IG9mIHRoZW1lIGluaGVyaXRzIGZyb20gb25lIG9mOiAKCi0gZWxlbWVudF90ZXh0ICh0ZXh0IGVsZW1lbnRzIGxpa2UgZm9udCwgY29sb3VyLCBzaXplLCBmYWNlIChib2xkLCBpdGFsaWNzKSwgYWxpZ25tZW50KSwgCi0gZWxlbWVudF9saW5lIChncmlkIGxpbmVzLCBheGlzIGxpbmVzKSwgCi0gZWxlbWVudF9yZWN0IChwYW5lbHMgYW5kIGJhY2tncm91bmRzIC0gY29sb3VyLCBzaXplLCBmaWxsKSwgCi0gZWxlbWVudF9ibGFuayAoYXNzaWducyBub3RoaW5nLCB1c3VhbGx5IHdoZW4geW91IGFyZSB0cnlpbmcgdG8gZ2V0IHJpZCBvZiBzb21ldGhpbmcpLCAKLSBlbGVtZW50X2dyb2IgKG1ha2luZyBhIGdyaWQgZ3JvYikuCgpnZ3Bsb3QgY29tZXMgd2l0aCBzb21lIHRoZW1lcyAtIHNvIEkgd291bGQgc3VnZ2VzdCBzdGFydGluZyB3aXRoIHRoZSBvbmUgdGhhdCBpcyBjbG9zZXN0IHRvIHdoYXQgeW91IHdhbnQsIGFuZCBzdGFydCBtb2RpZnlpbmcgZnJvbSB0aGVyZS4KCkNoZWNrIG91dCB0aGVzZSB0aGVtZXM6Ci0gdGhlbWVfbWluaW1hbCgpCi0gdGhlbWVfY2xhc3NpYygpCi0gdGhlbWVfYncoKQotIHRoZW1lX3ZvaWQoKQotIHRoZW1lX2RhcmsoKQotIHRoZW1lX2dyYXkoKQotIHRoZW1lX2xpZ2h0KCkKCllvdSBjYW4gbG9vayBhdCB0aGUgZGVmYXVsdCBmb3IgZWFjaCB0aGVtZSBzaW1wbHkgYnkgdHlwaW5nIGl0IGludG8gdGhlIGNvbnNvbGUuCgpgYGB7cn0KdGhlbWVfYncKYGBgCgpBbmQgdGhpcyBpcyB3aGF0IGB0aGVtZV9idygpYCBsb29rcyBsaWtlOgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQoKcCArIHRoZW1lX2J3KCkKCgpgYGAKCkhlcmUgaXMgYW4gZXhhbXBsZSBvZiB0aGVtZV9kYXJrLgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQoKcCArIHRoZW1lX2RhcmsoKQpgYGAKVGhlcmUgYXJlIGFsc28gdGhlbWVzIHRoYXQgaGF2ZSBiZWVuIGNyZWF0ZWQgYW5kIHB1dCBpbnRvIGEgcGFja2FnZSwgZ2d0aGVtZXMuIFNvbWUgb2YgdGhlc2UgdGhlbWVzIGFyZSBiYXNlZCBvZmYgb2YgZ3JhcGhzIHNlZW4gaW4gcHJpbnQgb3Igb24gd2Vic2l0ZXMgKHRoZSBlY29ub21pc3QsIHdhbGwgc3RyZWV0IGpvdXJuYWwsIGZpdmV0aGlydHllaWdodCkgb3IgdG8gbWF0Y2ggc3RhbmRhcmQgdG9vbHMgKGV4Y2VsLCBnb29nbGUgZG9jcykuIEluZm9ybWF0aW9uIGFib3V0IHRoZSB0aGVtZXMgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vZ2l0aHViLmNvbS9qcm5vbGQvZ2d0aGVtZXMuCgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KGdndGhlbWVzKQpwICsgdGhlbWVfZWNvbm9taXN0KCkKYGBgCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KcCArIHRoZW1lX3N0YXRhKCkKYGBgCgpZb3UgY2FuIGFsc28gbWFrZSB5b3VyIG93biBjdXN0b20gdGhlbWUgYXMgZGVtb2VkIGhlcmU6IGh0dHA6Ly9qb2V5c3RhbmxleS5jb20vYmxvZy9jdXN0b20tdGhlbWVzLWluLWdncGxvdDIKCgpJIGFtIGdvaW5nIHRvIHNob3cgeW91IGhvdyB0byBtb2RpZnkgYSBwbG90LCBzdGFydGluZyBmcm9tIHRoZW1lX21pbmltYWwoKSBiZWNhdXNlIEkgZG9uJ3QgbGlrZSB0aGUgZ3JleSBiYWNrZ3JvdW5kcyBvciBoYXJzaCBheGlzIGxpbmVzLgoKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KcCArIHRoZW1lX21pbmltYWwoKQoKYGBgCgoKVGhpbmdzIEkgZG9uJ3QgbGlrZSBhYm91dCB0aGlzIHBsb3QgYW5kIHRoZWlyIHNvbHV0aW9uczoKCnxQcm9ibGVtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBTb2x1dGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnxfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX186fDpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXzp8Cnx4LWF4aXMgbGFiZWxzIG92ZXJsYXAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCByb3RhdGUgbGFibGVzOiBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9OTAsIGhqdXN0PTEpICAgICAgICAgICAgICB8Cnxjb3VudHJ5IGxhYmVscyBhcmUgc21hbGxlciB0aGFuIGF4aXMgbGFiZWxzICAgICAgICAgICAgICAgfCBjaGFuZ2Ugc2l6ZSBhbmQgZmFjZTogc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNil8Cnx0aXRsZSBpcyB1bmNlbnRlcmVkICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBhZGp1c3QgaG9yaXpvbnRhbGx5OiBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZSA9IDE4KSAgICAgICB8Cnxib3JkZXIgdG8gc2VwYXJhdGUgY291bnRyaWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBjcmVhdGUgYSBib3JkZXI6IHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpICAgICAgICAgICAgICAgICAgICB8CnxtYWtlIHkgYXhpcyB0aWNrcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBjcmVhdGUgeSBheGlzIHRpY2tzOiBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2xpbmUoKSAgICAgICAgICAgICAgICAgICAgICAgICB8CgoKSXQgaXNuJ3QgbmVjZXNzYXJ5IHRvIHJlbWVtYmVyIGFsbCBvZiB0aGlzIHN5bnRheCwgSSBmcmVxdWVudGx5IGZpbmQgbXlzZWxmIGJhY2sgYXQgdGhlIGhlbHAgcGFnZSBhdCBodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS90aGVtZS5odG1sLgoKVGhlIGxhc3QgY2FsbCB0byB0aGVtZSB3aWxsIG92ZXJyaWRlIHByZXZpb3VzIGNhbGxzLiBUaGVyZWZvcmUsIGlmIHdlIHdhbnQgdG8gc3RhcnQgd2l0aCBgdGhlbWVfbWluaW1hbCgpYCBhcyBvdXIgYmFzZSwgaXQgaGFzIHRvIGJlIGluIG91ciBjb2RlIEJFRk9SRSB0aGUgb3RoZXIgbW9kaWZpY2F0aW9ucy4gCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmdncGxvdChkYXRbZGF0JFRheGEgJWluJSBrZWVwJFRheGEsXSAsIGFlcyh4ID0gVGF4YSwgeSA9IE9UVXMsIGZpbGwgPSBUYXhhKSkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXIgPSAicmVkIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID05MCwgaGp1c3Q9MSksIAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpLCAKICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE2KSwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemUgPSAxOCksIAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfbGluZSgpKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGZhY2V0X2dyaWQofkNvdW50cnksIGxhYmVsbGVyID0gbGFiZWxsZXIoQ291bnRyeSA9IGxhYmVscykpICsKICBnZ3RpdGxlKCJBYnVuZGFuY2Ugb2YgVGF4YSBieSBDb3VudHJ5IikgKwogIHhsYWIoTlVMTCkgKwogIHlsYWIoImxvZyhPVFVzKSIpICsgCiAgZ3VpZGVzKGZpbGw9RkFMU0UpCgpgYGAKCioqKgpfX0NoYWxsZW5nZV9fIAoKCjxkaXYgc3R5bGU9ImZsb2F0OmxlZnQ7bWFyZ2luOjAgMTBweCAxMHB4IDAiIG1hcmtkb3duPSIxIj4KIVtdKGltZy9QdWctdnMtRm9vZC0xLmpwZyl7d2lkdGg9MTUwcHh9Cgo8L2Rpdj4KCkNoYW5nZSB0aGUgY29sb3VyIG9mIHRoZSBwbG90IGJhY2tncm91bmQuIEFkZCBtaW5vciBncmlkbGluZXMsIGFuZCBtYWtlIHRoZSBtYWpvciBncmlkbGluZXMgYmxhY2suIEluY3JlYXNlIHRoZSBzcGFjaW5nIGluIGJldHdlZW4gdGhlIHBhbmVscy4KCgo8L2JyPgo8L2JyPgo8L2JyPgoKKioqCgojIyNDb3JyZWxhdGlvbiBQbG90cyBhbmQgSGVhdG1hcHMKCk9uZSBjb21tb24gd2F5IHRvIGxvb2sgZm9yIG91dGxpZXJzIG9yIHRyZW5kcyBpbiBkYXRhIGlzIHRvIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBzYW1wbGVzIGFuZCBtYWtlIGEgY29ycmVsYXRpb24gcGxvdC4gU2luY2Ugc2FtcGxlcyBhcmUgZXhwZWN0ZWQgdG8gYmUgaW4gY29sdW1ucywgdGhpcyBpcyBhbiBleGFtcGxlIG9mIHdoZXJlIHlvdSB3b3VsZCB1c2UgJ3dpZGUnIGRhdGEuIFRoZSBkZWZhdWx0IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGlzIHRoZSBQZWFyc29uIGNvcnJlbGF0aW9uLCBidXQgb3RoZXJzIChLZW5kYWxsLCBTcGVhcm1hbikgY2FuIGJlIHNwZWNpZmllZC4gTm90ZSB0aGF0IHRoZSBlbnRpcmUgbWF0cml4IG11c3QgYmUgbnVtZXJpYy4gCgpBIGhlYXRtYXAgY2FuIGJlIG1hZGUgaW4gZ2dwbG90IGJ5IHVzaW5nIHRoZSB0aWxlIGdlb20uIFRoZSBmaWxsIGlzIG5vdyB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQsIHdoaWNoIGlzIGEgY29udGludW91cyB2YXJpYWJsZSAoaGVuY2UgdGhlIGdyYWRpZW50KS4gSSBoYXZlIGNoYW5nZWQgdGhlIHRpdGxlIG9mIHRoZSBsZWdlbmQgZnJvbSAndmFsdWUnIHRvICdQZWFyc29uIENvcnJlbGF0aW9uJyBieSB1c2luZyBgc2NhbGVfZmlsbF9jb250aW51b3VzKClgLiBUaGUgJ1xuJyBpbmNsdWRlZCBpbiB0aGUgbmFtZSBpcyBhIGxpbmUgYnJlYWsuIEEgZmFudGFzdGljIHJlc291cmNlIGZvciBtb2RpZnlpbmcgbGVnZW5kcyBpcyBodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9MZWdlbmRzXyhnZ3Bsb3QyKS8uCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmNkYXQgPC0gcmVhZC5jc3YoImRhdGEvRU5WX3BpdGxhdHJpbmUuY3N2Iiwgcm93Lm5hbWVzID0gMSkKCmNkYXQgPC0gY29yKGNkYXQpCgpsaWJyYXJ5KHJlc2hhcGUyKQptZWx0IDwtbWVsdChjZGF0KQoKZ2dwbG90KG1lbHQsIGFlcyhWYXIxLCBWYXIyLCBmaWxsID0gdmFsdWUpKSArIAogIGdlb21fdGlsZSgpICsKICB4bGFiKE5VTEwpICsKICB5bGFiKE5VTEwpICsKICBzY2FsZV9maWxsX2NvbnRpbnVvdXMobmFtZSA9ICJQZWFyc29uXG5Db3JyZWxhdGlvbiIpCgpgYGAKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KY2RhdCA8LSByZWFkLmNzdigiZGF0YS9TUEVfcGl0bGF0cmluZS5jc3YiLCByb3cubmFtZXMgPSAxKQoKY2RhdCA8LSBjb3IoY2RhdCkKCmxpYnJhcnkocmVzaGFwZTIpCm1lbHQgPC1tZWx0KGNkYXQpCgpnZ3Bsb3QobWVsdCwgYWVzKFZhcjEsIFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsgCiAgZ2VvbV90aWxlKCkgKwogIHhsYWIoTlVMTCkgKwogIHlsYWIoTlVMTCkgKwogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIlBlYXJzb25cbkNvcnJlbGF0aW9uIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID05MCwgaGp1c3Q9MSkpIAogIApgYGAKCgpJZiB0aGlzIGxvb2tzIGEgbGl0dGxlIGphcnJpbmcgdG8geW91IGNvbXBhcmVkIHRvICdub3JtYWwnIGhlYXRtYXBzLCBpdCBpcyBwcm9iYWJseSBiZWNhdXNlIHRoZSBkYXRhIGhhcyBub3QgYmVlbiBvcmRlcmVkIHlldC4gVGhpcyBjYW4gYmUgZG9uZSBieSBjYWxjdWxhdGluZyB0aGUgZGlzdGFuY2UgYmV0d2VlbiBzYW1wbGVzIChpZS4gZXVjbGlkZWFuKSBhbmQgdGhlbSBjbHVzdGVyaW5nIHRoZW0gaW4gYSBoaWVyYXJjaGljYWwgd2F5IChpZS4gY29tcGxldGUgbGlua2FnZSkuCgpUbyBoYXZlIGEgZGlmZmVyZW50IGNvbG91ciBzY2hlbWUgdGhhdCBmb3IgZXhhbXBsZSwgZ29lcyBmcm9tIHJlZCB0byB5ZWxsb3csIHlvdSBjYW4gdXNlIGBzY2FsZV9maWxsX2dyYWRpZW50KClgIGFuZCBzcGVjaWZ5IHdoYXQgeW91IHdhbnQgdGhlIGRpdmVyZ2luZyBjb2xvdXJzIHRvIGJlLiBOb3RlIHRoYXQgeW91ciBsZWdlbmQgdGl0bGUgbm93IGRlcml2ZXMgZnJvbSB0aGlzIGdyYWRpZW50LiAKCgpgYGB7cn0KZGQgPC0gZGlzdChjZGF0KQpoYyA8LSBoY2x1c3QoZGQpCmNkYXQgPC0gY2RhdFtoYyRvcmRlciwgaGMkb3JkZXJdCgptZWx0IDwtbWVsdChjZGF0KQoKZ2dwbG90KG1lbHQsIGFlcyhWYXIxLCBWYXIyLCBmaWxsID0gdmFsdWUpKSArIGdlb21fdGlsZSgpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KG5hbWUgPSAiUGVhcnNvblxuQ29ycmVsYXRpb24iLCBsb3cgPSAicmVkIiwgaGlnaCA9ICJ5ZWxsb3ciKSArCiAgeGxhYihOVUxMKSArCiAgeWxhYihOVUxMKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9OTAsIGhqdXN0PTEsIHNpemUgPTUpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9NSkpIAoKYGBgCgpXZSBjYW4gbm93IHNlZSB0aGF0IHRoZXJlIGlzIGEgcGF0Y2ggb2YgZGFyayByZWQgc2FtcGxlcyB3aXRoIGxvdyBjb3JyZWxhdGlvbi4gSXQgaXMgZGlmZmljdWx0IHRvIHNlZSB3aGljaCBzaXRlcyB0aGVzZSBhcmUgYXMgdGhlIG5hbWVzIGFyZSBhIGJpdCBzbWFsbC4gCgpFbnRlciB0aGUgaW50ZXJhY3RpdmUgaGVhdG1hcCEgYFBsb3RseWAgYWxsb3dzIGZvciBpbnRlcmFjdGl2ZSBwbG90cyBpbiBhIHZhcmlldHkgb2YgbGFuZ3VhZ2VzLCBhbmQgYWxsb3dzIHRoZSBjcmVhdGlvbiBvZiBzaGFyYWJsZSBsaW5rcyAoaWYgeW91IGFyZSBpbnRlcmVzdGVkIHlvdSBoYXZlIHRvIHNpZ24gdXAgZm9yIGEgZnJlZSBhY2NvdW50IHRvIGdldCBBUEkgY3JlZGVudGlhbHMgaHR0cHM6Ly9wbG90Lmx5L3IvZ2V0dGluZy1zdGFydGVkLykuIFRoZXkgaGF2ZSBjcmVhdGVkIGEgcGFja2FnZSwgd2hpY2gsIGx1Y2tpbHkgZm9yIHVzLCBpbnRlcmZhY2VzIHNlYW1sZXNzbHkgd2l0aCBgZ2dwbG90MmAuIEkgb25seSBuZWVkIHRvIHNhdmUgbXkgcGxvdCBhbmQgY2FsbCB0aGUgZnVuY3Rpb24gYGdncGxvdGx5KClgIHRvIGhhdmUgYW4gaW50ZXJhY3RpdmUgZ3JhcGghIAoKVGhlIHJlc3BvbnNlIG9mIHBsb3RseSB0byBtYW5pcHVsYXRpb25zIG90aGVyIHRoYW4gaG92ZXJpbmcgY2FuIGJlIGEgYml0IHNsb3cgaWYgbm90IHVzaW5nIHRoZSBBUEkgYW5kIG1heSBmcmVlemUvY3Jhc2ggUiwgc28gSSByZWNvbW1lbmQgc2F2aW5nIHlvdXIgY29kZSBiZWZvcmUgdHJ5aW5nIGl0LiAgCgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KHBsb3RseSkKcCA8LSBnZ3Bsb3QobWVsdCwgYWVzKFZhcjEsIFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsgZ2VvbV90aWxlKCkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobmFtZSA9ICJQZWFyc29uXG5Db3JyZWxhdGlvbiIsIGxvdyA9ICJyZWQiLCBoaWdoID0gInllbGxvdyIpICsKICB4bGFiKE5VTEwpICsKICB5bGFiKE5VTEwpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID05MCwgaGp1c3Q9MSksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgCgpnZ3Bsb3RseShwKQoKYGBgCgpIb3ZlcmluZyB3aWxsIGhpZ2hsaWdodCB0aGUgdmFyaWFibGVzIGFuZCB2YWx1ZXMgcmVsYXRpbmcgdG8gdGhhdCBzcGVjaWZpYyBkYXRhIHBvaW50LCBhbmQgYXJlYXMgY2FuIGJlIHpvb21lZCBpbiBvbi4gV2UgY2FuIG5vdyBzZWUgdGhlIG5hbWVzIG9mIHRoZSBsb3cgY29ycmVsYXRpb24gc2FtcGxlcy4gV2hpbGUgdGhleSBhcmUgYWxsIGZyb20gVGFuemFuaWEsIHRoZXkgZG8gbm90IGFwcGVhciB0byBhbGwgYmUgZnJvbSB0aGUgc2FtZSBMYXRyaW5lX051bWJlciBvciBEZXB0aC4gCgpgZ2d2aXNgIGlzIGEgQmlvY29uZHVjdG9yIHBhY2thZ2UgYWxsb3dpbmcgaW50ZXJhY3RpdmUgZ3JhcGhpY3MgaW4gUiAoaHR0cDovL2dndmlzLnJzdHVkaW8uY29tLykuIEl0IGlzIGZvdW5kZWQgb24gdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MgYW5kIHRoZSBzeW50YXggaXMgc2ltaWxhciwgYnV0IG5vdCBpZGVudGljYWwgdG8gYGdncGxvdDJgLiBJdCBpcyBnb29kIGZvciBpbnRlcmFjdGl2ZSBzY2F0dGVycGxvdHMsIGJhciBncmFwaHMsIGxpbmUgZ3JhcGhzIGFuZCBoaXN0b2dyYW1zIChhbHRob3VnaCBub3QgaGVhdG1hcHMgYXQgdGhpcyBwb2ludCBpbiB0aW1lKS4gVGhlIG5hdHVyZSBvZiB0aGUgaW50ZXJhY3RpdmVuZXNzIGlzIGhpZ2hseSBjdXN0b21pemFibGU6IHlvdSBjYW4gaG92ZXIsIGFkZCBzbGlkZXIgYmFycywgY2hlY2tib3hlcyBvciByYWRpb2J1dHRvbnMgKGl0IHVzZXMgU2hpbnkncyByZWFjdGl2ZSBwcm9ncmFtbWluZykuCgpJbiB0aGlzIGV4YW1wbGUgSSBoYXZlIGp1c3QgdGFrZW4gYSBzY2F0dGVycGxvdCB0aGF0IHdlIG1hZGUgZWFybGllciwgYW5kIG1hZGUgaXQgcG9zc2libGUgdG8gaG92ZXIgb3ZlciBhIHBvaW50IGFuZCBzZWUgaXRzIHVuZGVybHlpbmcgZGF0YS4gRG9uJ3Qgd29ycnkgYWJvdXQgdGhlIGZ1bmN0aW9uIGFuZCBzeW50YXggdG9vIG11Y2ggLSB0aG9zZSB3aWxsIGJlIGludHJvZHVjZWQgaW4gbGF0ZXIgbGVzc29ucy4gSSBqdXN0IHdhbnRlZCB0byBzaG93IHlvdSBhbm90aGVyIG9wdGlvbiBmb3IgaW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbiB0aGF0IGhvcGVmdWxseSBpc24ndCB0b28gaW50aW1pZGF0aW5nLiAKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShnZ3ZpcykKCnAgPC0gZ2d2aXMobmRhdCwgeD0gflRTLCB5PSB+Q09EdCkgCiAgCmxheWVyX3BvaW50cyhwKSAlPiUgYWRkX3Rvb2x0aXAoZnVuY3Rpb24oeCkgYyhwYXN0ZTAoIlRTPSIsIHgkVFMsICIsIENPRFQ9IiwgeCRDT0R0KSkpCgpgYGAKIyMjU2F2aW5nIGluIGdncGxvdDIKCmdncGxvdDIgaGFzIGl0cyBvd24gZnVuY3Rpb24gZm9yIHNhdmluZyBnZ3NhdmUoKS4gCgpZb3UgY2FuIHNlbmQgdGhlIHBsb3Qgb2JqZWN0IHRvIHRoZSBncmFwaGljcyBkZXZpY2UgYW5kIHRoZW4gc2F2ZSB0aGF0IGltYWdlLiAKCldpdGggZ2dzYXZlIHlvdSBjYW4gbWluaW1hbGx5IGlucHV0IHRoZSBmaWxlbmFtZSB5b3Ugd291bGQgbGlrZSB0byBoYXZlLCBhbmQgdGhlIHBhdGggdG8geW91ciBmaWxlICh5b3UgY2FuIGluc3RlYWQgY29tYmluZSB0aGUgcGF0aCBhbmQgZmlsZW5hbWUgaW4gdGhlIGZpbGVuYW1lKS4KCmBgYHtyfQpnZ3Bsb3QobWVsdCwgYWVzKFZhcjEsIFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsgZ2VvbV90aWxlKCkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobmFtZSA9ICJQZWFyc29uXG5Db3JyZWxhdGlvbiIsIGxvdyA9ICJyZWQiLCBoaWdoID0gInllbGxvdyIpICsKICB4bGFiKE5VTEwpICsKICB5bGFiKE5VTEwpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID05MCwgaGp1c3Q9MSwgc2l6ZSA9NSksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT01KSkgCgpnZ3NhdmUoImhlYXRtYXAucG5nIiwgcGF0aCA9ICJpbWciKQpgYGAKSG93ZXZlciwgaW4gc29tZSBjYXNlcyB5b3Ugd2FudCB0byB0YWlsb3IgeW91ciBvdXRwdXQuIFlvdSBjYW4gc3BlY2lmeSB0aGUgd2lkdGgsIGhlaWdodCBhbmQgdW5pdHMgb2YgeW91ciBpbWFnZSwgb3IgeW91IGNhbiBhcHBseSBhIHNjYWxpbmcgZmFjdG9yLiBZb3UgY2FuIGFsc28gc3BlY2lmeSB0aGUgcGxvdCBvYmplY3QgeW91IHdhbnQgdG8gc2F2ZSBpbnN0ZWFkIG9mIHdoYXRldmVyIHdhcyBvbiB5b3VyIGdyYXBoaWNzIGRldmljZSBsYXN0IHVzaW5nIHRoZSAncGxvdCcgcGFyYW1ldGVyLiBnZ3NhdmUoKSBjYW4gc2F2ZSBpbiBhIHZhcmlldHkgb2YgZmlsZSBleHRlbnNpb25zIGluY2x1ZGluZyBwZGYsIGpwZWcsIHRpZmYsIGJtcCwgc3ZnIG9yIHBuZy4KCmBgYHtyfQpobWFwIDwtIGdncGxvdChtZWx0LCBhZXMoVmFyMSwgVmFyMiwgZmlsbCA9IHZhbHVlKSkgKyBnZW9tX3RpbGUoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChuYW1lID0gIlBlYXJzb25cbkNvcnJlbGF0aW9uIiwgbG93ID0gInJlZCIsIGhpZ2ggPSAieWVsbG93IikgKwogIHhsYWIoTlVMTCkgKwogIHlsYWIoTlVMTCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPTkwLCBoanVzdD0xLCBzaXplID01KSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTUpKSAKCmdnc2F2ZSgiaGVhdG1hcC5wZGYiLCBwbG90ID0gaG1hcCwgcGF0aCA9ICJpbWciLCBzY2FsZSA9IDIsIHdpZHRoID0gMTUwLCBoZWlnaHQgPSAxMTAsIHVuaXRzID0gIm1tIikKYGBgCgogIAojI1Rha2luZyBpdCB1cCBhIG5vdGNoCgoKIyMjTXVsdGlwbGUgcGxvdHMgb24gb25lIHBhZ2UgKGllLiBmb3IgcHVibGljYXRpb24gaW1hZ2VzKQoKVGhlcmUgYXJlIGEgdmFyaWV0eSBvZiBtZXRob2RzIHRvIG1peCBtdWx0aXBsZSBncmFwaHMgb24gdGhlIHNhbWUgcGFnZSwgaG93ZXZlciBgZ2dwbG90YCBkb2VzIG5vdCB3b3JrIHdlbGwgd2l0aCBhbGwgb2YgdGhlbS4gSSBhbSBnb2luZyB0byB3b3JrIHdpdGggYSBwYWNrYWdlIGJhc2VkIHRoYXQgdXNlcyBgZ3JpZEV4dHJhYCh3aGljaCBhbGxvd3MgdXMgdG8gYXJyYW5nZSBwbG90cykgYnV0IHdvcmtzIHdlbGwgd2l0aCBgZ2dwbG90YCBjYWxsZWQgYGdncHVicmAgKHdoaWNoIGFsbG93cyB1cyB0byBhbGlnbiB0aGUgYXhlcyBvZiBvdXIgcGxvdHMpLiBGb3IgYSBkZW1vbnN0cmF0aW9uLCB3ZSBhcmUgZ29pbmcgdG8gdGFrZSAzIHBsb3RzIHRoYXQgd2UgbWFkZSBlYXJsaWVyIChhIGJveHBsb3QsIGEgaGlzdG9ncmFtLCBhbmQgYSBidWJibGUgcGxvdCksIHNhdmUgdGhlbSBhcyBvYmplY3RzLCBhbmQgdGhlbiBhcnJhbmdlIGFuZCBhbGlnbiB0aGVtIGluIHRoZSBzYW1lIGZpZ3VyZS4gKGh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvcnBrZ3MvZ2dwdWJyLykKClRoZXJlIGlzIGEgZnVuY3Rpb24gYGdnYXJyYW5nZSgpYCB3aGljaCB0YWtlcyB5b3VyIHBsb3RzLCB0aGVpciBsYWJlbHMsIGFuZCBob3cgdGhleSBhcmUgYXJyYW5nZWQgaW4gcm93cyBhbmQgY29sdW1ucy4gVG8gc3RhcnQsIGlmIHdlIGp1c3QgaGF2ZSBvdXIgYm94cGxvdCBhbmQgYnViYmxlIHBsb3QsIGFuZCB3ZSB3YW50ZWQgdGhlbSB0byBiZSBzaWRlIGJ5IHNpZGUsIGlmIHlvdSBwaWN0dXJlIGVhY2ggcGxvdCBhcyBhIHNxdWFyZSBpbiBhIGdyaWQsIHdlIHdvdWxkIGhhdmUgdHdvIGNvbHVtbnMgYG5jb2wgPSAyYCBhbmQgMSByb3cgCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkoZ2dwdWJyKQoKaGlzdCA8LSBnZ3Bsb3QoZGF0W2RhdCRPVFUgPj0yLF0sIGFlcyh4PU9UVXMsIGZpbGw9Q291bnRyeSwgYWxwaGE9MC4zKSkgKyAKCWdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTAsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIHhsaW0oMCwxMDAwKSArCiAgeWxpbSgwLDE1MCkgKwogIGdlb21fcnVnKCkgCgpidWIgPC0gZ2dwbG90KGJkYXQsIGFlcyh4ID0gQ291bnRyeSwgeSA9IE9UVXNfcGVyX1RheGEsIGZpbGw9VGF4YSkpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGdlb21faml0dGVyKGFlcyhzaXplID0gc3FydChPVFVzX3Blcl9UYXhhL3BpKSksIHBjaCA9IDIxLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZT1jKDEsMzApKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IFRheGEpLCBjaGVja19vdmVybGFwID0gVFJVRSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkKCmdnYXJyYW5nZShidWIsIGhpc3QsIAogICAgICAgICAgbGFiZWxzID0gYygiQSIsICJCIiksCiAgICAgICAgICBuY29sID0gMiwgbnJvdyA9IDEpCgoKYGBgCldoaWxlIG91ciBncmlkIGFyZWFzIGFyZSB0aGUgc2FtZSBzaXplLCBvdXIgcGxvdCBiYWNrZ3JvdW5kcyBhcmUgbm90LiBMZXQncyBhZGp1c3QgdGhlIGxlZ2VuZCBzbyB0aGF0IGl0IGlzIGluIHRoZSB0b3AgcmlnaHQgY29ybmVyIG9mIHRoZSBwbG90LCBhbmQgZ2V0IHJlbW92ZSB0aGUgd2hpdGUgYmFja2dyb3VuZC4gV2UgZG8gbm90IG5lZWQgdGhlIGFscGhhIGxlZ2VuZC4gRG8geW91IHJlbWVtYmVyIGhvdyB0byBnZXQgcmlkIG9mIGl0PwoKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KaGlzdCA8LSBnZ3Bsb3QoZGF0W2RhdCRPVFUgPj0yLF0sIGFlcyh4PU9UVXMsIGZpbGw9Q291bnRyeSwgYWxwaGE9MC4zKSkgKyAKCWdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTAsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIHhsaW0oMCwxMDAwKSArCiAgeWxpbSgwLDE1MCkgKwogIGdlb21fcnVnKCkgKwogIGd1aWRlcyhhbHBoYSA9IEZBTFNFKSArCiAgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb249YygxLDEpLCBsZWdlbmQucG9zaXRpb249YygxLDEpLCBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKCmdnYXJyYW5nZShidWIsIGhpc3QsIAogICAgICAgICAgbGFiZWxzID0gYygiQSIsICJCIiksCiAgICAgICAgICBuY29sID0gMiwgbnJvdyA9IDEpCmBgYApJbWFnaW5lIGEgc3F1YXJlIHdpdGggNCBib3hlcy4gV2UgYXJlIGdvaW5nIHRvIHB1dCBvdXIgYm94cGxvdCBpbiB0aGUgbGVmdCB0b3AgYW5kIGJvdHRvbSBib3hlcywgdGhlIGhpc3RvZ3JhbSBpbiB0aGUgdG9wIHJpZ2h0IGJveCwgYW5kIHRoZSBidWJibGUgcGxvdCBpbiB0aGUgYm90dG9tIHJpZ2h0IGJveC4gIFRvIGRvIHRoaXMsIHdlIGFyZSBhcnJhbmdpbmcgMiBjb2x1bW5zIChvbmUgd2l0aCB0aGUgYm94cGxvdCBhbmQgb25lIHdpdGggdGhlIGhpc3RvZ3JhbSArIGJveHBsb3QpIGFuZCB3ZSBhcmUgYXJyYW5naW5nIDIgcm93cyAob25lIHdpdGggdGhlIGhpc3RvZ3JhbSBhbmQgb25lIHdpdGggdGhlIGJveHBsb3QpLgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQpib3ggPC0gZ2dwbG90KGRhdFsoZGF0JFRheGE9PSJDbG9zdHJpZGlhIiB8IGRhdCRUYXhhID09ICJHYW1tYXByb3Rlb2JhY3RlcmlhIiB8IGRhdCRUYXhhID09ICJVbmtub3duIiB8IGRhdCRUYXhhID09ICJCYWNpbGxpIikgJiBkYXQkQ291bnRyeSA9PSAiVCIsXSAsIGFlcyh4ID0gVGF4YSwgeSA9IE9UVXMpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgCiAgc2NhbGVfeV9sb2cxMCgpCgpnZ2FycmFuZ2UoYm94LCBnZ2FycmFuZ2UoaGlzdCwgYnViLCAKICAgICAgICAgIGxhYmVscyA9IGMoIkIiLCAiQyIpLAogICAgICAgICAgbnJvdyA9IDIpLAogICAgICAgICAgIG5jb2wgPSAyLCBsYWJlbHMgPSAiQSIpCgpgYGAKVGhlIHktYXhpcyBsaW5lcyBmb3IgdGhlIGhpc3RvZ3JhbSBhbmQgYm94cGxvdCBhcmUgbm90IGFsaWduZWQsIGJ1dCB0aGlzIGNhbiBiZSBmaXhlZCB3aXRoIGEgY2FsbCB0byBgYWxpZ249InYiYC4KCgpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmdnYXJyYW5nZShib3gsIGdnYXJyYW5nZShoaXN0LCBidWIsIAogICAgICAgICAgbGFiZWxzID0gYygiQiIsICJDIiksCiAgICAgICAgICBucm93ID0gMiwgYWxpZ24gPSAidiIpLAogICAgICAgICAgIG5jb2wgPSAyLCBsYWJlbHMgPSAiQSIpCgpgYGAKVG8gbWFrZSBzdXJlIGFsbCBheGlzIHRpdGxlcyBhcmUgdGhlIHNhbWUgc2l6ZSAoQiBhbmQgQyBsb29rIGEgbGl0dGxlIG9mZiB0byBtZSksIHdlIGNhbiBzcGVjaWZ5IHRoZSBmb250IHdlIHdhbnQgY2hhbmdlZCBhbmQgdGhlIHNpemUgd2Ugd2FudCB0byBjaGFuZ2UgaXQgdG8uIEkgYW0gYWxzbyBnb2luZyB0byBtYWtlIHRoZSBsZWdlbmQgdGl0bGUgc2l6ZSB0aGUgc2FtZS4gSSB3YW50IHRvIG1ha2UgdGhlIGJ1YmJsZSBwbG90IGZvbnQgYSBsaXR0bGUgc21hbGxlciBhcyB3ZWxsLiBMZXQncyBsb29rIGF0IHRoZSBmb250IGZ1bmN0aW9uLiBDYW4gSSBkbyB0aGF0PyBXaGF0IG1pZ2h0IGJlIGFub3RoZXIgb3B0aW9uPwpgYGB7ciB3YXJuaW5nID0gRkFMU0V9CmJ1YiA8LSBnZ3Bsb3QoYmRhdCwgYWVzKHggPSBDb3VudHJ5LCB5ID0gT1RVc19wZXJfVGF4YSwgZmlsbD1UYXhhKSkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHNpemUgPSBzcXJ0KE9UVXNfcGVyX1RheGEvcGkpKSwgcGNoID0gMjEsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlPWMoMSwzMCkpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gVGF4YSwgc2l6ZT0xNiksIGNoZWNrX292ZXJsYXAgPSBUUlVFLCBzaG93LmxlZ2VuZCA9IEZBTFNFKQoKCmdnYXJyYW5nZShib3ggKyBmb250KCJheGlzLnRpdGxlIiwgc2l6ZT05KSwgZ2dhcnJhbmdlKGhpc3QgKyBmb250KCJheGlzLnRpdGxlIiwgc2l6ZT05KSArIGZvbnQoImxlZ2VuZC50aXRsZSIsIHNpemU9OSksIGJ1YiArIGZvbnQoImF4aXMudGl0bGUiLCBzaXplPTkpLCAKICAgICAgICAgIGxhYmVscyA9IGMoIkIiLCAiQyIpLAogICAgICAgICAgbnJvdyA9IDIsIGFsaWduID0gInYiKSwKICAgICAgICAgICBuY29sID0gMiwgbGFiZWxzID0gIkEiKQpgYGAKCioqKgpfX0NoYWxsZW5nZV9fIAoKCjxkaXYgc3R5bGU9ImZsb2F0OmxlZnQ7bWFyZ2luOjAgMTBweCAxMHB4IDAiIG1hcmtkb3duPSIxIj4KIVtdKGltZy9QdWctdnMtRm9vZC0xLmpwZyl7d2lkdGg9MTUwcHh9Cgo8L2Rpdj4KClVzaW5nIHRoZSBjb2RlIGF2YWlsYWJsZSBoZXJlIGZvciBhIGxpbmUgZ3JhcGggd2UgbWFkZSBlYXJsaWVyIGluIHRoZSBsZXNzb24sIG1ha2UgYSBjb21iaW5lZCBmaWd1cmUuIEltYWdpbmUgYSBzcXVhcmUgd2l0aCA0IGJveGVzLiBXZSBhcmUgZ29pbmcgdG8gcHV0IG91ciBsaW5lIGdyYXBoIGluIHRoZSB0b3AgcmlnaHQgYW5kIGxlZnQgYm94ZXMsIHRoZSBoaXN0b2dyYW0gaW4gdGhlIGJvdHRvbSBsZWZ0IGJveCwgYW5kIHRoZSBidWJibGUgcGxvdCBpbiB0aGUgYm90dG9tIHJpZ2h0IGJveC4gTWFrZSBzdXJlIHRoZSBsZWdlbmQgYW5kIGF4aXMgdGl0bGVzIGFyZSB0aGUgc2FtZSBzaXplLiBDaGFuZ2UgdGhlIGxlZ2VuZCB0ZXh0IGZvciB0aGUgbGluZSBncmFwaCB0byBiZSBzbWFsbGVyIHRoYW4gdGhlIGxlZ2VuZCB0aXRsZS4gCgpgYGAKbGluIDwtIGdncGxvdChlcnJkYXQsIGFlcyh4PVRlbXAsIHk9cEgsIGNvbG91ciA9IENvdW50cnkpKSArIAogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1wSC1zZF9wSCwgeW1heCA9IHBIK3NkX3BIKSwgd2lkdGggPSAuMiwgYWxwaGEgPSAwLjQpCmBgYAoKPC9icj4KPC9icj4KPC9icj4KCioqKgoKYGBge3IgaW5jbHVkZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0KbGluIDwtIGdncGxvdChlcnJkYXQsIGFlcyh4PVRlbXAsIHk9cEgsIGNvbG91ciA9IENvdW50cnkpKSArIAogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1wSC1zZF9wSCwgeW1heCA9IHBIK3NkX3BIKSwgd2lkdGggPSAuMiwgYWxwaGEgPSAwLjQpCgoKZ2dhcnJhbmdlKGxpbisgZm9udCgiYXhpcy50aXRsZSIsIHNpemU9OSkgKyBmb250KCJsZWdlbmQudGl0bGUiLCBzaXplPTkpICsgZm9udCgibGVnZW5kLnRleHQiLCBzaXplID0gOCksIGdnYXJyYW5nZShoaXN0ICsgZm9udCgiYXhpcy50aXRsZSIsIHNpemU9OSkgKyBmb250KCJsZWdlbmQudGl0bGUiLCBzaXplPTkpLCBidWIgKyBmb250KCJheGlzLnRpdGxlIiwgc2l6ZT05KSwgCiAgICAgICAgICBsYWJlbHMgPSBjKCJCIiwgIkMiKSwKICAgICAgICAgIG5jb2wgPSAyLCBhbGlnbiA9ICJoIiksCiAgICAgICAgICAgbnJvdyA9IDIsIGxhYmVscyA9ICJBIikKYGBgCgoKClRoZXJlIGFyZSBtYW55IGZhbnRhc3RpYyBSIHBhY2thZ2VzIHRvIGFuYWx5emUgYW5kIHZpc3VhbGl6ZSBfeW91cl8gZGF0YS4gQXMgYSBncm91cCwgd2UgYXJlIGxpa2VseSB3b3JraW5nIG9uIGEgdmFyaWV0eSBvZiBzcGVjaWFsaXplZCBhcmVhcy4gVGhlIHBsb3RzIHdlIGhhdmUgbWFkZSBzbyBmYXIgdG9kYXkgc2hvdWxkIGJlIHVzZWZ1bCBmb3IgZGF0YSBleHBsb3JhdGlvbiBmb3IgbWFueSBkaWZmZXJlbnQga2luZHMgb2YgZGF0YS4gSW4gdGhlIG5leHQgc2VjdGlvbiB3ZSBhcmUgZ29pbmcgdG8gcHJldmlldyBzb21lIG1vcmUgY29tcGxleCB2aXN1YWxpemF0aW9uIHR5cGVzLCBidXQgc2luY2UgdGhlc2UgdGFrZSBtb3JlIHRpbWUgdG8gZ28gdGhyb3VnaCBhbmQgbm90IGV2ZXJ5b25lIG1heSBiZSBpbnRlcmVzdGVkIGluIG5ldHdvcmsgZGlhZ3JhbXMsIHRpbWUtc2VyaWVzIGFuYWx5c2lzLCBvciBnZW9zcGF0aWFsIGRhdGEsIHdlIHdpbGwgbm90IGJlIHBsb3R0aW5nIGFsbCBvZiB0aGVzZSBvdXJzZWx2ZXMuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBhIHR1dG9yaWFsIG9uIG9uZSBvZiB0aGVzZSB2aXN1YWxpemF0aW9uIHR5cGVzLCBwbGVhc2UgaW5kaWNhdGUgdGhhdCBpbiB0aGUgY29tbWVudHMgYXJlYSBvZiB0aGUgbGVzc29uIHN1cnZleSAoaHR0cHM6Ly93d3cuc3VydmV5bW9ua2V5LmNvbS9yL1ZOUVozS1MpLiAKCldlIHdpbGwsIGhvd2V2ZXIgZG8gYW4gVXBzZXQgUGxvdCBhcyBteSBwZXJzb25hbCBjcnVzYWRlIHRvIGhhdmUgc29tZXRoaW5nIGJldHRlciB0aGFuIHZlbm4gZGlhZ3JhbXMgaW4gcHVibGljYXRpb25zLgoKCiMjI0ludGVyYWN0aXZlIGdyYXBoaWNzIAoKUGxvdGx5OgotIGh0dHBzOi8vcGxvdC5seS9yLwoKZ2d2aXM6Ci0gaHR0cDovL2dndmlzLnJzdHVkaW8uY29tL2ludGVyYWN0aXZpdHkuaHRtbAoKSGVhdG1hcHM6Ci0gaHR0cHM6Ly9naXRodWIuY29tL3RhbGdhbGlsaS9oZWF0bWFwbHkKCkludGVyYWN0aXZlIHRpbWUtc2VyaWVzIGRhdGE6Ci0gaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9keWdyYXBocy8KIyMjTmV0d29yayBkaWFncmFtcyAKdmlzTmV0d29yayAoYmFzZWQgb24gaWdyYXBoKQogIGh0dHBzOi8vZGF0YXN0b3JtLW9wZW4uZ2l0aHViLmlvL3Zpc05ldHdvcmsvZWRnZXMuaHRtbAoKIyMjQ2lyY29zIFBsb3RzCk1pZ2VzdAogIGh0dHBzOi8vZ2phYmVsLndvcmRwcmVzcy5jb20vY2F0ZWdvcnkvci9taWdlc3QvCgojIyNVcHNldCBQbG90cwotIFVwU2V0UiBodHRwczovL2dpdGh1Yi5jb20vaG1zLWRibWkvVXBTZXRSCgpVcHNldCBwbG90cyBhcmUgYW4gYWx0ZXJuYXRpdmUgdG8gdmVubiBkaWFncmFtcyB0aGF0IHNob3cgdGhlIGludGVyc2VjdGlvbiBvZiBzZXRzLCBhcyB3ZWxsIGFzIHRoZSBzaXplIG9mIHRoZSBzZXRzLiBBZGRpdGlvbmFsbHksIHZlbm4gZGlhZ3JhbXMgY2FuIGJlIGRpZmZpY3VsdCB0byBpbnRlcnByZXQgZm9yIGdyZWF0ZXIgdGhhbiAyIG9yIDMgc2V0cy4KCkxldCdzIHNlZSBob3cgdGhpcyB3b3JrcyBwcmFjdGljYWxseS4gVGhlIHF1ZXN0aW9uIHRoYXQgd2UgYXJlIGFza2luZyBpcyBob3cgbWFueSBzaXRlcyBhcmUgZWFjaCBUYXhhIHJlcHJlc2VudGVkIGF0IGFuZCB3aGF0IGlzIHRoZSBvdmVybGFwIHdpdGggb3RoZXIgVGF4YT8gVGhlIGRhdGEgdGhhdCB3ZSBoYXZlIGlzIGFuIE9UVSB0YWJsZS4gRm9yIHRoaXMgcHVycG9zZSB3ZSBzaW1wbHkgd2FudCB0byBrbm93IHdoZXRoZXIgYSBnaXZlbiBUYXhhIHdhcyBwcmVzZW50IG9yIGFic2VudCBpbiB0aGUgc2FtcGxlLCBhbmQgdGhlbiB3ZSBjYW4gZm9ybSBpbnRlcnNlY3Rpb25zIGJhc2VkIG9uIHRoaXMgaW5mb3JtYXRpb24uIFNvLCBmb3IgZWFjaCBPVFUgdmFsdWUgdGhhdCBpcyBub3QgMCwgd2UgcmVwbGFjZSBpdCB3aXRoIGEgMSBpbnN0ZWFkLiBUaGVuIGFsbCB3ZSBoYXZlIHRvIGRvIGlzIHRvIGVudGVyIG91ciBkYXRhIHNldCwgdGhlIG51bWJlciBvZiBzZXRzIHdlIGFyZSBpbnB1dHRpbmcsIGlmIHdlIHdhbnQgdG8gb3JkZXIgdGhlIHJlc3VsdHMgKGluIHRoaXMgY2FzZSBieSBmcmVxdWVuY3kpLCBhbmQgaG93IG1hbnkgaW50ZXJzZWN0aW9ucyB3ZSB3YW50IHRvIHNob3cuIEkgaGF2ZSBwaWNrZWQgOCBkaWZmZXJlbnQgVGF4YSwgd2hpY2ggd291bGQgbWFrZSBhIGNyYXp5IHZlbm4gZGlhZ3JhbSB3aXRoIGEgbG90IG9mIHplcm9zIGluIGl0IHNpbmNlIGEgY291cGxlIG9mIHRoZXNlIFRheGEgYXJlIHJhcmUuIEhlcmUsIEkgd2lsbCBzaG93IDE1IGludGVyc2VjdGlvbnMgLSB3ZSBrbm93IHRoZSByZW1haW5pbmcgaW50ZXJzZWN0aW9ucyB3b3VsZCBiZSB6ZXJvIHNpbmNlIHRoaXMgaXMgb3JkZXJlZCBieSBmcmVxdWVuY3kuCgpgYGB7cn0KbGlicmFyeShVcFNldFIpCgpzdWJkYXQgPC0gcmVhZC5jc3YoImRhdGEvU1BFX3BpdGxhdHJpbmUuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKdGVzdCA8LSBzdWJkYXQgJT4lIAogICByb3duYW1lc190b19jb2x1bW4gJT4lIAogICBnYXRoZXIodmFyLCB2YWx1ZSwgLXJvd25hbWUpICU+JSAKICAgc3ByZWFkKHJvd25hbWUsIHZhbHVlKSAKCgpjb2xuYW1lcyh0ZXN0KSA8LSB0ZXN0WzMwLF0KdGVzdCA8LSB0ZXN0Wy0zMCxdCgp0ZXN0WywtMV0gPC0gYXBwbHkodGVzdFssLTFdLCAyLCBmdW5jdGlvbih4KSBpZmVsc2UoYXMubnVtZXJpYyh4KSE9MCwgMSwgMCkpCnRlc3QgPC0gdGVzdFssIGMoMSw0LDksMTcsMjIsMTksMjAsMzMsMzUpXQoKCnVwc2V0KHRlc3QsIG5zZXRzID0gOCwgZW1wdHkuaW50ZXJzZWN0aW9ucyA9IFRSVUUsIG9yZGVyLmJ5ID0gImZyZXEiLCBuaW50ZXJzZWN0cyA9IDE1LCBtYWluLmJhci5jb2xvciA9ICJibGFjayIsIHNldHMuYmFyLmNvbG9yID0gIiM1NkI0RTkiICkKCmBgYApMZXQncyBsb29rIGF0IG91ciBncmVhdGVzdCBJbnRlcnNlY3Rpb24gU2l6ZSAoZXF1YWwgdG8gMjgpLiBUaGlzIG1lYW5zIHRoYXQgMjggb2Ygb3VyIDgxIHNhbXBsZXMgaGF2ZSBDbG9zdHJpZGlhLCBBbHBoYXByb3Rlb2JhY3RlcmlhLCBhbmQgRGVpbm9jb2NjaSBwcmVzZW50IGluIHRoZSBzYW1lIHNhbXBsZSBXSVRIT1VUIENobG9yb2ZsZXhpLCBib3RoIEFjaWRvYmFjdGVyaWEsIEN5YW5vYmFjdGVyaWEgb3IgTGVudGlzcGhhZXJpYSBhbHNvIGJlaW5nIHByZXNlbnQuIFdlIGNhbiBzZWUgZnJvbSB0aGUgU2V0IFNpemUgdGhhdCBDbG9zdHJpZGlhIGFuZCBBbHBoYXByb3Rlb2JhY3RlcmlhIGFyZSBwcmVzZW50IGluIGFsbW9zdCBhbGwgc2FtcGxlcywgYW5kIERlaW5vY29jY2kgaXMgcHJlc2VudCBpbiBncmVhdGVyIHRoYW4gMy80IG9mIHRoZSBzYW1wbGVzLiBZb3UgbWlnaHQgZXhwZWN0IHRoaXMgb3ZlcmxhcCB0byBiZSBoaWdoZXIsIGJ1dCByZW1lbWJlciB0aGF0IHRoaXMgaXMgd2l0aG91dCB0aGUgb3RoZXIgVGF4YSBhbmQgbW92aW5nIGFsb25nIG91ciBpbnRlcmVzZWN0aW9uIHNpemVzIGFuZCBkb3QgbWF0cml4LCB3ZSBjYW4gc2VlIHRoYXQgb3RoZXIgaW50ZXJzZWN0aW9ucyBpbmNsdWRlIHRoZXNlIGJhY3RlcmlhLiBTb21lIHF1aWNrIG1lbnRhbCBtYXRoIHNob3dzIHRoYXQgNjMgc2FtcGxlcyBoYXZlIHRoZXNlIDMgYmFjdGVyaWEgcHJlc2VudC4gIAoKIyMjR2Vvc3BhdGlhbCBEYXRhClN0YXRpYyBNYXBzOgotIGh0dHBzOi8vYmhhc2thcnZrLmdpdGh1Yi5pby91c2VyMjAxNy5nZW9kYXRhdml6L25vdGVib29rcy8wMi1TdGF0aWMtTWFwcy5uYi5odG1sCgpJbnRlcmFjdGl2ZSBNYXBzOgotIGh0dHBzOi8vYmhhc2thcnZrLmdpdGh1Yi5pby91c2VyMjAxNy5nZW9kYXRhdml6L25vdGVib29rcy8wMy1JbnRlcmFjdGl2ZS1NYXBzLm5iLmh0bWwKICAKIyMjUGh5bG9nZW5ldGljcyBEYXRhIApnZ3RyZWU6IAotIGh0dHA6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy8zLjcvYmlvYy92aWduZXR0ZXMvZ2d0cmVlL2luc3QvZG9jL3RyZWVBbm5vdGF0aW9uLmh0bWwjYW5ub3RhdGUtY2xhZGVzCgp0cmVlbWFuOiAKLSBodHRwczovL2JtY3Jlc25vdGVzLmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvczEzMTA0LTAxNi0yMzQwLTgKCm1ldGFjb2RlcjogCi0gaHR0cHM6Ly9naXRodWIuY29tL2dydW53YWxkbGFiL21ldGFjb2RlcgoKcGh5bG9zZXE6Ci0gaHR0cHM6Ly9qb2V5NzExLmdpdGh1Yi5pby9waHlsb3NlcS9pbmRleC5odG1sCgojIyNHZW5vbWljcyBEYXRhIApnZ2JpbzogCi0gaHR0cDovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzLzIuMTEvYmlvYy92aWduZXR0ZXMvZ2diaW8vaW5zdC9kb2MvZ2diaW8ucGRmCgpHZW5WaXNSOgotIGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0dlblZpc1IvaW5zdC9kb2MvR2VuVmlzUl9pbnRyby5odG1sCgpHZW5vbWVHcmFwaHM6Ci0gaHR0cHM6Ly9ibWNiaW9pbmZvcm1hdGljcy5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2LzE0NzEtMjEwNS0xMC0yCiAgIAoqKioKX19DaGFsbGVuZ2VfXyAKCgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvUHVnLXZzLUZvb2QtMS5qcGcpe3dpZHRoPTE1MHB4fQoKPC9kaXY+CgpJbnN0YWxsIGFuZCBsb2FkIHRoZSBnYXBtaW5kZXIgcGFja2FnZS4gU2F2ZSB0aGUgZ2FwbWluZGVyIGRhdGEgaW50byBhbiBvYmplY3QgdXNpbmcgYGRhdCA8LSBnYXBtaW5kZXJgLiBQbG90IHRoZSBmb2xsb3dpbmc6CgogIDEuIGEgcXVhbGl0YXRpdmUgdmFyaWFibGUgd2l0aCBhIHF1YW50aXRhdGl2ZSB2YXJpYWJsZSAoYXQgbGVhc3QgMiB3YXlzKS4gCiAgICArIEZhY2V0IHlvdXIgcGxvdCBieSB0aGUgcXVhbGl0YXRpdmUgdmFyaWFibGUuCiAgMi4gdHdvIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgKGF0IGxlYXN0IDIgd2F5cykuIAogICAgKyBBZGQgYSByZWdyZXNzaW9uIGxpbmUgdG8geW91ciBwbG90LgoKPC9icj4KPC9icj4KPC9icj4KCioqKgoKCl9fUmVzb3VyY2VzOl9fICAgICAKCldpY2toYW0sIEhhZGxleS4gKDIwMTApLiBBIExheWVyZWQgR3JhbW1hciBvZiBHcmFwaGljcy4gSm91cm5hbCBvZiBDb21wdXRhdGlvbmFsIGFuZCBTdGF0aXN0aWNhbCBHcmFwaGljcy4gICAgIApXaWxraW5zb24sIEwuICgyMDA1KSwgVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MgKDJuZCBlZC4pLiBTdGF0aXN0aWNzIGFuZCBDb21wdXRpbmcsIE5ldyBZb3JrOiBTcHJpbmdlci4gWzE0LCAxOF0gICAgICAgICAgCmh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzLyAgICAgCmh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2dncGxvdDItdHV0b3JpYWwgICAgIApUdWZ0ZSwgRWR3YXJkIFIuIFRoZSBWaXN1YWwgRGlzcGxheSBvZiBRdWFudGl0YXRpdmUgSW5mb3JtYXRpb24uICAgICAgCmh0dHA6Ly9zdGNvcnAubmwvUl9jb3Vyc2UvdHV0b3JpYWxfZ2dwbG90Mi5odG1sICAgICAKaHR0cDovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvdGhlbWUuaHRtbApodHRwOi8vam9leXN0YW5sZXkuY29tL2Jsb2cvY3VzdG9tLXRoZW1lcy1pbi1nZ3Bsb3QyCmh0dHBzOi8vZ2l0aHViLmNvbS9qcm5vbGQvZ2d0aGVtZXMKaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvfmZyYXppZXIvUlNwYXRpYWxHdWlkZXMvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYKaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dncmVwZWwvdmlnbmV0dGVzL2dncmVwZWwuaHRtbApodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9MZWdlbmRzXyhnZ3Bsb3QyKS8KaHR0cHM6Ly9naXRodWIuY29tL2VjbGFya2UvZ2diZWVzd2FybQpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dzY2kvdmlnbmV0dGVzL2dnc2NpLmh0bWwKaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9ycGtncy9nZ3B1YnIvCmh0dHBzOi8vcnB1YnMuY29tL2Ryc29uZy85NTc1Cmh0dHA6Ly9lbHB1Yi5iaWIudW5pLXd1cHBlcnRhbC5kZS9lZG9jcy9kb2t1bWVudGUvZmJiL3dpcnRzY2hhZnRzd2lzc2Vuc2NoYWZ0L3NkcC9zZHAxNS9zZHAxNTAwNi5wZGYKaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9hcnRpY2xlcy8yNC1nZ3B1YnItcHVibGljYXRpb24tcmVhZHktcGxvdHMvODEtZ2dwbG90Mi1lYXN5LXdheS10by1taXgtbXVsdGlwbGUtZ3JhcGhzLW9uLXRoZS1zYW1lLXBhZ2UvCgoKI1Bvc3QtTGVzc29uIEFzc2Vzc21lbnQKKioqCgpZb3VyIGZlZWRiYWNrIGlzIGVzc2VudGlhbCB0byBoZWxwIHRoZSBuZXh0IGNvaG9ydCBvZiB0cmFpbmVlcy4gUGxlYXNlIHRha2UgYSBtaW51dGUgdG8gY29tcGxldGUgdGhlIGZvbGxvd2luZyBzaG9ydCBzdXJ2ZXk6Cmh0dHBzOi8vd3d3LnN1cnZleW1vbmtleS5jb20vci9WTlFaM0tTCgo8L2JyPgoKKioqCgo8L2JyPgoKVGhhbmtzIGZvciBjb21pbmchISEKCiFbXShpbWcvcnN0dWRpby1ib21iLnBuZyl7d2lkdGg9MzAwcHh9